• Welcome to PlanetSquires Forums.
 

WinFBE using CWindow #6

Started by Paul Squires, June 18, 2016, 07:31:08 PM

Previous topic - Next topic

Paul Squires

I have attached the latest source code and binaries. A lot of work has gone into this version of the code and the editor is now almost to a usable state. Menu and Toolbar states are now working. The majority of the menu items have now been implemented (notable exceptions being the find/replace/goto/MRU and compile options). My first try at syntax coloring and code folding has been implemented. File dirty buffer states are now correctly handled when files are closed or the application ends. Configuration data is now saved and restored using full unicode variables and disk file.

Lots more to do.

Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca

Looking very good.

A couple of tips:

1) If an user has been working with e.g. 96 DPI with the editor using all the height of the working area and decides to change its DPI setting to a higher value, the bottom of the editor will go outside the monitor and he will be unable to resize it unless he deletes the .ini file.

In my editor I did include a menu option called "Restore Main Window Size" that adjusts the size to the working area:


      CASE IDM_RESTOREWSIZE
         DIM rc AS RECT
         SystemParametersInfo SPI_GETWORKAREA, 0, @rc, 0
         MoveWindow hwnd, 0, 0, rc.Right - rc.Left, rc.Bottom - rc.Top, CTRUE
         AfxCenterWindow hwnd


2) Another problem can happen if the user is using more than one monitor and changes the physical order of the displays. This can change the desktop coordinates from zero-based to negative, making the editor invisible because it will be positioned off-screen. Calling the function AfxForceVisibleDisplay (in AfxWin.inc) should solve it.


' ========================================================================================
' If you use dual (or even triple/quad) displays then you have undoubtedly encountered the
' following situation: You change the physical order of your displays, or otherwise
' reconfigure the logical ordering using your display software. This sometimes has the
' side-effect of changing your desktop coordinates from zero-based to negative starting
' coordinates (i.e. the top-left coordinate of your desktop changes from 0,0 to -1024,-768).
' This effects many Windows programs which restore their last on-screen position whenever
' they are started. Should the user reorder their display configuration this can sometimes
' result in a Windows program subsequently starting in an off-screen position (i.e. at a
' location that used to be visible) - and is now effectively invisible, preventing the
' user from closing it down or otherwise moving it back on-screen.
' The ForceVisibleDisplay function can be called at program start-time right after the
' main window has been created and positioned 'on-screen'. Should the window be positioned
' in an off-screen position, it is forced back onto the nearest display to its last
' position. The user will be unaware this is happening and won't even realise to thank you
' for keeping their user-interface visible, even though they changed their display
' settings.
' Source: http://www.catch22.net/tuts/tips2
' ========================================================================================
PRIVATE SUB AfxForceVisibleDisplay (BYVAL hwnd AS HWND)
   ' // Check if the specified window-rectangle is visible on any display
   DIM rc AS RECT
   GetWindowRect(hwnd, @rc)
   IF MonitorFromRect(@rc, MONITOR_DEFAULTTONULL) <> NULL THEN EXIT SUB
   ' // Find the nearest display to the rectangle
   DIM hMonitor AS HMONITOR
   DIM mi AS MONITORINFO
   mi.cbSize = SIZEOF(mi)
   hMonitor = MonitorFromRect(@rc, MONITOR_DEFAULTTONEAREST)
   GetMonitorInfoW(hMonitor, @mi)
   ' // Center window rectangle
   rc.left = mi.rcWork.left + ((mi.rcWork.right - mi.rcWork.left) - (rc.right-rc.left)) \ 2
   rc.top = mi.rcWork.top + ((mi.rcWork.bottom - mi.rcWork.top) - (rc.bottom-rc.top)) \ 2
   SetWindowPos(hwnd, 0, rc.left, rc.top, 0, 0, SWP_NOACTIVATE OR SWP_NOZORDER OR SWP_NOSIZE)
END SUB
' ========================================================================================


Paul Squires

Excellent, thanks Jose! I have implemented both of your suggestions and it seems to be working very well.

:)
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca

#3
This is enough. We don't need to call AfxCenterWindow.


      CASE IDM_RESTOREWSIZE
         DIM rc AS RECT
         SystemParametersInfo SPI_GETWORKAREA, 0, @rc, 0
         MoveWindow hwnd, 0, 0, rc.Right - rc.Left, rc.Bottom - rc.Top, CTRUE


These are little details that don't happen often, but when it happens they are very annoying for the user.

Also, in general, we have to be careful when displaying a dialog or another tool, to no exceed the bottom area. Otherwise, buttons like "Ok", "Apply" or "Cancel" aren't visible. Life was much easier when the resolution was 600x800.


José Roca

Other options that I use when loading in the editor files that have been written and saved in Linux, are replacing tabs with spaces and converting end of line characters.

Converting line characters is very easy: you just send a message to the Scintilla control, e.g. SendMessageA (hSci, SCI_CONVERTEOLS, eolMode, 0), where eolMode can be SC_EOL_CRLF, SC_EOL_CR, or SC_EOL_LF. As I work only with Windows, the one that I use is SendMessageA (hSci, SCI_CONVERTEOLS, SC_EOL_CRLF, 0).

José Roca

#5
Two small changes to your color combobox code.

In frmOptionsColor.inc


     Case WM_MEASUREITEM
         Scope
         If (wParam = IDC_FRMOPTIONSCOLORS_CBCOLOR1) OrElse _
            (wParam = IDC_FRMOPTIONSCOLORS_CBCOLOR2) OrElse _
            (wParam = IDC_FRMOPTIONSCOLORS_COMBOFONTNAME) Then
            Dim pWindow As CWindow Ptr = AfxCWindowPtr(HWnd)
            Dim pMeasureItem As MEASUREITEMSTRUCT Ptr = Cast(MEASUREITEMSTRUCT Ptr, lParam)
'            pMeasureItem->itemHeight = pMeasureItem->itemHeight * pWindow->rxRatio
            pMeasureItem->itemHeight = pMeasureItem->itemHeight * pWindow->ryRatio
            Return True
         End If
         End Scope


It should be ryRatio instead of rxRatio. Doesn't matter in Windows, because the two values are identical, but... I did the same mistake in my code.

In modCbColor.inc


         Select Case lpdis->itemAction
            Case ODA_DRAWENTIRE, ODA_SELECT
               DIM pWindow AS CWindow PTR = AfxCWindowPtr(hwnd)
               ' Clear background
               FillRect lpdis->hDC, @lpdis->rcItem, GetSysColorBrush(COLOR_WINDOW)
               ' Set the font
               SelectFont( lpdis->hDC, AfxCWindowPtr(HWnd)->Font)
               ' Set text background
               SetBkColor lpdis->hDC, GetSysColor(COLOR_WINDOW)
               ' Set text color
               SetTextColor lpdis->hDC, GetSysColor(COLOR_WINDOWTEXT)
               rc = lpdis->rcItem
               rc.Left = pWindow->ScaleX(28)
               DrawTextW lpdis->hDC, @wColorName, Len(wColorName), @rc, DT_SINGLELINE Or DT_LEFT Or DT_VCENTER
               ' Selected item
               If (lpdis->itemState And ODS_SELECTED) Then    ' // If selected
                  If (lpdis->itemState And &H1000) = 0 Then   ' // If not ODS_COMBOBOXEDIT (= &H1000)
                     rc.Left   = pWindow->ScaleX(26)
                     rc.Right  = lpdis->rcItem.Right
                     rc.Top    = lpdis->rcItem.Top
                     rc.Bottom = lpdis->rcItem.Bottom
                     ' Invert area around text only
                     InvertRect lpdis->hDC, @rc
                  End If
                  'and draw a focus rectangle around all
                  DrawFocusRect lpdis->hDC, @lpdis->rcItem
               End If

               ' color rectangle (using RoundRect for nicer looks..
               If (lpdis->itemState And &H1000) Then          ' // If ODS_COMBOBOXEDIT (= &H1000)
                  ' Set coordinates
                  rc.Left  = pWindow->ScaleX(4)
                  rc.Right = pWindow->ScaleX(24)
               Else
                  ' A tiny bit to the left in list
                  rc.Left  = pWindow->ScaleX(3)
                  rc.Right = pWindow->ScaleX(23)
               End If
               rc.Top    = lpdis->rcItem.Top + pWindow->ScaleY(2)
               rc.Bottom = lpdis->rcItem.Bottom - pWindow->ScaleY(2)
               ' Create brush with proper color
               hBrush = CreateSolidBrush(nColorValue)
               ' Select brush into device context
               hBrush = SelectObject( Cast(HDC, lpdis->hDC), hBrush)
               ' Draw
               RoundRect lpdis->hDC, rc.Left, rc.Top, rc.Right, rc.Bottom, AfxScaleY(3), AfxScaleY(3)
               ' Select old brush and delete the new one
               DeleteObject SelectObject(lpdis->hDC, hBrush)


Get a pointer to CWindow with AfxCWindowPtr and use pWindow->ScaleX instead of AfxScale. AfxScale always uses the DPI of the monitor, whereas pWindows->Scalex uses the DPI that we have chosen, which is useful when we cheat about it to test applications at different DPIs without changing the settings of the computer.

For example, when I use pWindow->DPI = 96, the coloured rectangles became too wide (see capture).

José Roca

With the proposed change, they scale to the right size.

José Roca

The simulation trick isn't perfect because the Windows' UI elements will scale according the DPI settings of the computer (in the capture, a big window caption and a big listbox scroll bar), but it's very useful to test our code.

Paul Squires

Thanks Jose - great suggestions and I have updated the code with the changes. I also like the ideas of converting line ending and tabs to spaces. I will add that code tonight as well.
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer