• Welcome to PlanetSquires Forums.
 

WinFBE using CWindow #5

Started by Paul Squires, June 14, 2016, 08:50:54 PM

Previous topic - Next topic

Paul Squires

Hi Everyone,

Here is the latest source and exe/dll to try. As you can see, I have been busy trying to add functionality to the editor. You can now add a new file or open (one or more) disk files. That's it for the functionality on the File menu, however I have implemented a fair amount of the Edit and View menu functionality. Of course, lots left to do and not much error checking built in there yet.

It is *32 bit* only at this point because the Scintilla dll is 32-bit.

Edit: Updated June 15, 2016 to include all of Jose's suggestions below.
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca

A few changes:


   ' Add the top tabcontrol (initially not visible)
'   gTTabCtl.hWindow = pWindow->AddControl("TABCONTROL", pWindow->hWindow, _
'       IDC_FRMMAIN_TOPTABCONTROL, "", 0, 0, 0, pWindow->ScaleY(24), _
'       WS_CHILD Or WS_TABSTOP Or TCS_SINGLELINE Or TCS_RAGGEDRIGHT Or TCS_HOTTRACK Or TCS_TABS Or TCS_FOCUSNEVER, _
'       WS_EX_LEFT Or WS_EX_LTRREADING)

   gTTabCtl.hWindow = pWindow->AddControl("TABCONTROL", pWindow->hWindow, _
       IDC_FRMMAIN_TOPTABCONTROL, "", 0, 0, 0, 24, _
       WS_CHILD Or WS_TABSTOP Or TCS_SINGLELINE Or TCS_RAGGEDRIGHT Or TCS_HOTTRACK Or TCS_TABS Or TCS_FOCUSNEVER, _
       WS_EX_LEFT Or WS_EX_LTRREADING)


The height doesn't have to be scaled because the AddControl method will do it.


' ========================================================================================
' Position all child windows. Called manually and/or by WM_SIZE
' ========================================================================================
Function frmMain_PositionWindows( ByVal HWnd As HWnd ) As LRESULT

'   Dim pWindow As CWindow Ptr = AfxCWindowPtr(HWnd)

   Dim As HWnd hEdit, hTabCtl
   Dim As Rect rc
   Dim As Long nHeightRebar, nHeightStatusBar, nHeightTabControl

   ' Get the entire client area
'   rc = pWindow->GetClientRect()
   GetClientRect(hwnd, @rc)

   ' Get the heights of the rebar and statusbar
'   nHeightRebar = pWindow->ControlClientHeight( GetDlgItem(HWnd, IDC_FRMMAIN_REBAR) )
'   nHeightStatusBar = pWindow->ControlClientHeight( GetDlgItem(HWnd, IDC_FRMMAIN_STATUSBAR) )
   nHeightRebar = AfxGetWindowHeight( GetDlgItem(HWnd, IDC_FRMMAIN_REBAR) )
   nHeightStatusBar = AfxGetWindowHeight( GetDlgItem(HWnd, IDC_FRMMAIN_STATUSBAR) )

   ' If items exist in the top tabcontrol then show the tab control and account for its height
   hTabCtl = GetDlgItem(HWnd, IDC_FRMMAIN_TOPTABCONTROL)
   If TabCtrl_GetItemCount(hTabCtl) > 0 Then
'      nHeightTabControl = pWindow->ControlClientHeight(hTabCtl)
      nHeightTabControl = AfxGetWindowHeight(hTabCtl)
      SetWindowPos hTabCtl, 0, _
                     rc.Left, rc.Top + nHeightRebar, _
                     rc.Right, nHeightTabControl, _
                     SWP_SHOWWINDOW Or SWP_NOZORDER
   End If

   ' Reduce the height of the client area by the size of the rebar and statusbar.
   Dim pDoc As clsDocument Ptr = gTTabCtl.GetActiveDocument()
   If pDoc Then hEdit = pDoc->hWindow

   SetWindowPos hEdit, 0, _
                  rc.Left, rc.Top + nHeightRebar + nHeightTabControl, _
                  rc.Right, rc.Bottom - nHeightRebar - nHeightStatusBar - nHeightTabControl, _
                  SWP_SHOWWINDOW Or SWP_NOZORDER
   Function = 0
End Function


The CWindow metric methods return unscaled values, suitable to pass to another CWindow method that scales it (we don't want them to scale an already scaled value) but the API function SetWindowPos wants real values.

Alternate way, call CWindow SetWindowPos:


' ========================================================================================
' Position all child windows. Called manually and/or by WM_SIZE
' ========================================================================================
Function frmMain_PositionWindows( ByVal HWnd As HWnd ) As LRESULT

   Dim pWindow As CWindow Ptr = AfxCWindowPtr(HWnd)

   Dim As HWnd hEdit, hTabCtl
   Dim As Rect rc
   Dim As Long nHeightRebar, nHeightStatusBar, nHeightTabControl

   ' Get the entire client area
   rc = pWindow->GetClientRect()

   ' Get the heights of the rebar and statusbar
'   nHeightRebar = pWindow->ControlClientHeight( GetDlgItem(HWnd, IDC_FRMMAIN_REBAR) )
'   nHeightStatusBar = pWindow->ControlClientHeight( GetDlgItem(HWnd, IDC_FRMMAIN_STATUSBAR) )
   ' // We need the control height, not just the client area
   nHeightRebar = pWindow->ControlHeight( GetDlgItem(HWnd, IDC_FRMMAIN_REBAR) )
   nHeightStatusBar = pWindow->ControlHeight( GetDlgItem(HWnd, IDC_FRMMAIN_STATUSBAR) )

   ' If items exist in the top tabcontrol then show the tab control and account for its height
   hTabCtl = GetDlgItem(HWnd, IDC_FRMMAIN_TOPTABCONTROL)
   If TabCtrl_GetItemCount(hTabCtl) > 0 Then
'      nHeightTabControl = pWindow->ControlClientHeight(hTabCtl)
      nHeightTabControl = pWindow->ControlHeight(hTabCtl)
      pWindow->SetWindowPos hTabCtl, 0, _
                     rc.Left, rc.Top + nHeightRebar, _
                     rc.Right, nHeightTabControl, _
                     SWP_SHOWWINDOW Or SWP_NOZORDER
   End If

   ' Reduce the height of the client area by the size of the rebar and statusbar.
   Dim pDoc As clsDocument Ptr = gTTabCtl.GetActiveDocument()
   If pDoc Then hEdit = pDoc->hWindow

   pWindow->SetWindowPos hEdit, 0, _
                  rc.Left, rc.Top + nHeightRebar + nHeightTabControl, _
                  rc.Right, rc.Bottom - nHeightRebar - nHeightStatusBar - nHeightTabControl, _
                  SWP_SHOWWINDOW Or SWP_NOZORDER
   Function = 0
End Function


Using the unmodified code, see in the capture how it looks in my system.

José Roca

And after the change...

José Roca

Useful tip: If in the child dialogs you use the same DPI that the parent, ie.


Function frmOptionsCompiler_Show( ByVal hWndParent As HWnd, _
                                  ByVal nCmdShow   As Long = 0 _
                                  ) As Long

   '  Create the main window and child controls
   Dim pWindow As CWindow Ptr = New CWindow
   pWindow->DPI = AfxCWindowPtr(hwndParent)->DPI


You can test the effects of changing the DPI without having to change the DPI in your computer!

Just use


'  Create the main window and child controls
Dim pWindow As CWindow Ptr = New CWindow("WinFBE")
pWindow->DPI = 144   ' or any other value


If you do it, you will notice that you can have problems with the ownerdraw color comboboxes at certains DPIs.

But the most important is that you don't have to guess, just change the DPI value and you will notice visually any error.


José Roca

You could even allow the user to choose the scaling ratio in terms of percentage if you want.

José Roca

#5
The problem with the color comboboxes can be solved using pWindow->ScaleX/ScaleY instead of AfxScaleX.


   CreateCBColorList(pWindow->hWindow, IDC_FRMOPTIONSCOLORS_CBCOLOR1, _
                 pWindow->ScaleX(175), pWindow->ScaleY(41), pWindow->ScaleX(146), pWindow->ScaleY(20) )

   CreateCBColorList(pWindow->hWindow, IDC_FRMOPTIONSCOLORS_CBCOLOR2, _
                 pWindow->ScaleX(175), pWindow->ScaleY(100), pWindow->ScaleX(146), pWindow->ScaleY(20) )


With all these proposed changes, you can freely change the DPI used by the class (by default, the same used by the computer, but we can cheat) with pWidow.DPI = <some value> in the main form. I did that intentionally to be able to test the application at different DPIs without having to change the DPI setting of the computer.


Paul Squires

Excellent! Thanks for the great suggestion about setting the DPI without having to change the computer settings. Today, I actually did change the computer settings and I immediately saw the problem that you described above. I will always do the DPI tests from now as as a normal/regular part of my testing.

Thanks!
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

Paul Squires

Quote from: Jose Roca on June 15, 2016, 11:50:34 AM
Useful tip: If in the child dialogs you use the same DPI that the parent, ie.


Function frmOptionsCompiler_Show( ByVal hWndParent As HWnd, _
                                  ByVal nCmdShow   As Long = 0 _
                                  ) As Long

   '  Create the main window and child controls
   Dim pWindow As CWindow Ptr = New CWindow
   pWindow->DPI = AfxCWindowPtr(hwndParent)->DPI


You can test the effects of changing the DPI without having to change the DPI in your computer!

Just use


'  Create the main window and child controls
Dim pWindow As CWindow Ptr = New CWindow("WinFBE")
pWindow->DPI = 144   ' or any other value


If you do it, you will notice that you can have problems with the ownerdraw color comboboxes at certains DPIs.

But the most important is that you don't have to guess, just change the DPI value and you will notice visually any error.


This tip has made my day! What an incredibly useful and powerful technique! Now I can easily test different scalings by only changing one number in code rather than my computer settings. Also, having other forms automatically take the scaling of their parent is awesome.

Great job Jose. You the man.
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

Paul Squires

Original post updated with changes.
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca

Quote
This tip has made my day! What an incredibly useful and powerful technique! Now I can easily test different scalings by only changing one number in code rather than my computer settings. Also, having other forms automatically take the scaling of their parent is awesome.

You will need to use this feature, because working at 96 DPI CWindow does not any scaling and, therefore, you won't notice any error. I have been preaching about DPI to deaf ears during years.

José Roca

#10
Another use of this feature will be to adjust the window and controls size on the fly when using more than one monitor with different DPIs. Windows 8.1 and above allow per-monitor DPI. So, a window created at a certain DPI, will receive the WM_DPICHANGED message if it is moved to another monitor with a different DPI.

We could use pWindow->DPI = <new DPI value>, change the size of the window calling pWindow->SetClientSize, enumerate all the child controls and change the font size with AfxModifyFontSettings and resize the controls with pWindow->MoveWindow or pWindow->SetWindowPos.

New features require changes in the way of coding. Custom controls that don't allow dynamic changes are pretty useless for the new times. This is why I advice to allow the user to pass the scaling ratios. If you cache default values, allow to change them. You can't even assume that the DPI will remain constant while the application is running :)

Paul Squires

Holy cow - having to deal with different DPI's on multiple monitor setups. :)   I can see this being useful for the editor should I decide to implement the project explorer as a non-modal window that could be dragged to the second monitor.... and then have it magically re-display based on that monitor's DPI settings. Pretty cool.

I spent tonight working on (1) converting the config save/restore to use unicode and an utf16 encoded disk file, and (2) when saving filenames in the config file for each folder location determine if it resides on the same drive as the WinFBE application. If it does then substitute the replaceable parameter {CURDRIVE} for the drive letter. This allows you to easily run the editor on different media (eg. thumb drive) that may be assigned a different drive letter. I am currently using Jean-Pierre's utility (SwitchIniFile) to handle the different drive names.

Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer