Attached is the very first code for WinFBE that now uses Jose's CWindow class and associated functions. It is basically the main form of the editor. You can view this as a proof of concept.
To compile, you can edit the batch files and replace my path to the compilers with your own specific path. If you use JellyFB or another FB editor then remember to add WinFBE.rc to the compile.
@Paul,
One common problem is that I can't work with many programs unless I maximize them. Without doing it, the bottom of the editor goes outside my monitor.
What I do in my editor is to dimension the main widow according to the working area. Later, I save the window placement if the user has resized it. But how can resize it if the bottom is outside of the monitor?
This is what I use:
' Create the main window and child controls
Dim pWindow As CWindow
' // Retrieve the size of the working area
DIM rc AS RECT = pWindow.GetWorkArea
pWindow.Create(Null, "WinFBE - FreeBASIC Programmer's Editor", @frmMain_WndProc, _
0, 0, rc.Right - rc.Left, rc.Bottom - rc.Top)
pWindow.Center
I will have to do something with the icons, if I can manage to display them. For some reason they aren't displayed in my system.
BTW you can use your own class name, e.g.
Dim pWindow As CWindow = "WinFBE"
Well, if we fill the working area, then pWindow.Center isn't needed.
' Create the main window and child controls
Dim pWindow As CWindow
' // Retrieve the size of the working area
DIM rc AS RECT = pWindow.GetWorkArea
pWindow.Create(Null, "WinFBE - FreeBASIC Programmer's Editor", @frmMain_WndProc, _
0, 0, rc.Right - rc.Left, rc.Bottom - rc.Top)
Thanks Jose, I have made both changes.
So, you are not able to see the toolbar icons on your computer? I have attached a screenshot to this post to show what the application should look like. I am running Windows 10.
ImageList_AddMasked was failing in my computer (I'm using 192 DPI) because you have calculated the correct size for creating the image list, but then you are using a fixed size of 24 in ImageList_AddMasked.
i = ImageList_AddMasked( hImageListNormal, LoadImage( hInst, "IMAGE_N_NEW_FILE", IMAGE_BITMAP, 24, 24, nFlags ), cClr )
instead of
i = ImageList_AddMasked( hImageListNormal, LoadImage( hInst, "IMAGE_N_NEW_FILE", IMAGE_BITMAP, cx, cx, nFlags ), cClr )
Oh my, sometimes I can be so stupid! I should have seen that :)
Once you made the changes, how do the toolbar images look for you at 192 DPI ? Are they acceptable looking?
This happens in the best families. I also had forget to check for the validty of the pointer in the resize class. Normally, a good GPF reminds me that I have to do it, but not GPF in Windows 7.
Quote from: TechSupport on May 28, 2016, 07:38:56 PM
Once you made the changes, how do the toolbar images look for you at 192 DPI ? Are they acceptable looking?
Yes. They are simple images, without alpha channel, so no problem.
However, I'm going to convert the normal ones to PNGs and use my GDI+ functions to build the three image lists with just this icon set.
I have converted the icons to PNGs. They are included in images.rar. Put them in the Images/Toolbar folder.
In the WinFBE2.rar archive you will find WinFEB2.bas and WinFBE2.rc.
Even with these small icons, we save 62 Kb.
I also have added some icons to the File menu to demonstrate how to do it.
Bitmaps with pink background are a thing of the past.
If tomorrow you want to use alphablended icons, with this change you only have to get a set of new icons and recompile.
Awesome! The exe is indeed smaller. You must have made changes to the frmMain.inc file as well to use your new ImageList code for normal and dithered images?
Oh, the menu icons do not look very sharp on my system. I have attached a screenshot to show you.
Sorry, I have forgot to add that file. Of course it is the one that has most of the modifications.
> Oh, the menu icons do not look very sharp on my system. I have attached a screenshot to show you.
Strange. They look fine in mine.
Using Windows 7 or Windows 10?
I'm using only one set of icons.
For the gray images, I use:
i = ImageList_ReplaceIcon(hImageListDisabled, -1, AfxGdipIconFromRes(hInst, "IMAGE_NEW_FILE", 10, TRUE))
[code]
Where 10 is the porcentage of dimming (from 0 to 99) and TRUE indicates conversion to gray.
Please check if this menu looks right in your system.
When you use:
i = ImageList_ReplaceIcon(hImageListNormal, -1, AfxGdipIconFromRes(hInst, "IMAGE_COMMENT_REMOVE"))
Does the handle returned by AfxGdipIconFromRes have to be explicitly freed/destroyed? In my code using LoadImage, Windows would free the handle so I didn't have to do anything. I wonder does ImageList_ReplaceIcon use the handle you return or does it make a copy of the image a second time for its own use?
Quote from: Jose Roca on May 28, 2016, 10:19:36 PM
Please check if this menu looks right in your system.
It looks okay (but not great) see attached. It does not seem as sharp as yours. Maybe because you are using a much higher DPI than I am.
> It looks okay (but not great) see attached. It does not seem as sharp as yours. Maybe because you are using a much higher DPI than I am.
Must be because I'm using 48 bit icons that are the ones that I have at hand. Maybe for menus we must use smaller icons.
But what I wanted to know is if the problem with the menu using your editor icons was general or just because the icons are only 256 colors.
The menus use PARGB32 bitmaps.
The requirements are:
- The bitmap is a 32bpp DIB section.
- The DIB section has BI_RGB compression.
- The bitmap contains pre-multiplied alpha pixels.
My functions convert the PNG icons to PARGB32 bitmaps.
Maybe we may need two use at least two sets of icons, one for 96 to 144 and another for the rest. What we dont need is to have gray icons, because my functions allow to make them on the fly.
Of course, for best quality, nothing like using the appropriate icon size.
Quote from: TechSupport on May 28, 2016, 10:26:26 PM
When you use:
i = ImageList_ReplaceIcon(hImageListNormal, -1, AfxGdipIconFromRes(hInst, "IMAGE_COMMENT_REMOVE"))
Does the handle returned by AfxGdipIconFromRes have to be explicitly freed/destroyed? In my code using LoadImage, Windows would free the handle so I didn't have to do anything. I wonder does ImageList_ReplaceIcon use the handle you return or does it make a copy of the image a second time for its own use?
The MSDN documentations says
Quote
Because the system does not save hicon, you can destroy it after the function returns if the icon or cursor was created by the CreateIcon function. You do not need to destroy hicon if it was loaded by the LoadIcon function; the system automatically frees an icon resource when it is no longer needed.
Since we are creating the icon with GdipCreateHICONFromBitmap, not loading an existing one, guess that to be safe we can destroy it.
To avoid flicker, you can disable background erasing with:
' Create the main window and child controls
Dim pWindow As CWindow
' // Disable background erasing
pWindow.ClassStyle = CS_DBLCLKS
' // Retrieve the size of the working area
DIM rc AS RECT = pWindow.GetWorkArea
pWindow.Create(Null, "WinFBE - FreeBASIC Programmer's Editor", @frmMain_WndProc, _
0, 0, rc.Right - rc.Left, rc.Bottom - rc.Top)
I have modified the menu test to use 32 bit icons for the menu. Looks great in my system (at 192 DPI, 32 bit is the appropriate size). Don't know how will look in yours.
If it does not scale well, then we may need to use several icon sets of different sizes.
Of course this has nothing to do with regard to which icons you use in the editor (I'm not very picky about cosmetic details), but about exploring the best way to deal with the problem of icons size in toolbars, menus and other controls at different DPIs. Indeed, the best way is to use several sets of icons of different sizes, but I was trying to avoid this bloat.
Regarding the icons added to menis, you don't need to destroy them unless you specify FALSE in the fAutoDestroy parameter of the AfxAddIconToMenuItem.
If you look at the code of the AfxAddIconToMenuItem function you will see that the optional fAutoDestroy parameter defaults to TRUE and at the end of the function there is the following instruction: IF fAutoDestroy THEN DestroyIcon(hIcon).
' ========================================================================================
' Converts a hIcon to a bitmap and adds it to the specified hbmpItem field of HMENU item.
' The caller is responsible for destroying the bitmap generated. The icon will be destroyed
' if fAutoDestroy is set to true. The hbmpItem field of the menu item can be used to keep
' track of the bitmap by passing NULL to phbmp.
' Parameters:
' - hMenu = Menu handle that contains the item to which an icon will be added.
' - uItem = The identifier or position of the menu item to change.
' The meaning of this parameter depends on the value of fByPosition.
' - fByPosition = The meaning of nMenuItem. If this parameter is FALSE, nMenuItem is a
' menu item identifier. Otherwise, it is a menu item position.
' - hIcon = Handle of the icon to add to the menu.
' - fAutoDestroy = TRUE (the default) or FALSE.
' If TRUE, AddIconToMenuItem destroys the icon before returning.
' - phbmp = Location where the bitmap representation of the icon is stored. Can be NULL.
' ========================================================================================
#if _WIN32_WINNT = &h0602
PRIVATE FUNCTION AfxAddIconToMenuItem (BYVAL hMenu AS HMENU, BYVAL uItem AS DWORD, BYVAL fByPosition AS BOOLEAN, BYVAL hIcon AS HICON, BYVAL fAutoDestroy AS BOOLEAN = TRUE, BYVAL phbmp AS HBITMAP PTR = NULL) AS BOOLEAN
IF hMenu = NULL OR hIcon = NULL THEN EXIT FUNCTION
DIM hbmp AS HBITMAP, sizIcon AS SIZE, rcIcon AS RECT
sizIcon.cx = GetSystemMetrics(SM_CXSMICON)
sizIcon.cy = GetSystemMetrics(SM_CYSMICON)
SetRect(@rcIcon, 0, 0, sizIcon.cx, sizIcon.cy)
DIM hdcDest AS HDC = CreateCompatibleDC(NULL)
IF hdcDest = NULL THEN EXIT FUNCTION
DIM hr AS LONG = (AfxCreate32BitHBITMAP(hdcDest, @sizIcon, NULL, @hbmp))
IF hr THEN
DIM hbmpOld AS HBITMAP = CAST(HBITMAP, SelectObject(hdcDest, hbmp))
IF hbmpOld THEN
DIM bfAlpha AS BLENDFUNCTION = (AC_SRC_OVER, 0, 255, AC_SRC_ALPHA)
DIM paintParams AS BP_PAINTPARAMS
paintParams.cbSize = SIZEOF(paintParams)
paintParams.dwFlags = BPPF_ERASE
paintParams.pBlendFunction = @bfAlpha
DIM hdcBuffer AS HDC
DIM hPaintBuffer AS HPAINTBUFFER = BeginBufferedPaint(hdcDest, @rcIcon, BPBF_DIB, @paintParams, @hdcBuffer)
IF hPaintBuffer THEN
IF DrawIconEx(hdcBuffer, 0, 0, hIcon, sizIcon.cx, sizIcon.cy, 0, NULL, DI_NORMAL) THEN
' // If icon did not have an alpha channel, we need to convert buffer to PARGB32.
hr = AfxConvertBufferToPARGB32(hPaintBuffer, hdcDest, hIcon, sizIcon)
END IF
' // This will write the buffer contents to the destination bitmap.
EndBufferedPaint(hPaintBuffer, TRUE)
END IF
SelectObject(hdcDest, hbmpOld)
END IF
END IF
DeleteDC(hdcDest)
IF hr THEN hr = AfxAddBitmapToMenuItem(hMenu, uItem, fByPosition, hbmp)
IF hr = FALSE THEN DeleteObject(hbmp): hbmp = NULL
IF fAutoDestroy THEN DestroyIcon(hIcon)
IF phbmp THEN *phbmp = hbmp
FUNCTION = hr
END FUNCTION
#endif
' ========================================================================================
> Does the handle returned by AfxGdipIconFromRes have to be explicitly freed/destroyed?
A solution would be to write a function called AfxImageListReplaceIcon with an optional fAutoDestroy parameter like the one for menus.
See http://www.planetsquires.com/protect/forum/index.php?topic=3838.new#new
With the new functions, instead of
ImageList_ReplaceIcon(hImageList, -1, AfxGdipIconFromFile(ExePath & "\arrow_left_256.png"))
ImageList_ReplaceIcon(hImageList, -1, AfxGdipIconFromRes(hInst, "IDI_ARROW_LEFT_48"))
we can use
AfxGdipAddIconFromFile(hImageList, ExePath & "\arrow_left_256.png")
AfxGdipAddIconFromRes(hImageList, hInst, "IDI_ARROW_LEFT_48")
and don't worry about destroying the temporary icon.
Quote from: Jose Roca on May 29, 2016, 05:26:42 PM
See http://www.planetsquires.com/protect/forum/index.php?topic=3838.new#new
With the new functions, instead of
ImageList_ReplaceIcon(hImageList, -1, AfxGdipIconFromFile(ExePath & "\arrow_left_256.png"))
ImageList_ReplaceIcon(hImageList, -1, AfxGdipIconFromRes(hInst, "IDI_ARROW_LEFT_48"))
we can use
AfxGdipAddIconFromFile(hImageList, ExePath & "\arrow_left_256.png")
AfxGdipAddIconFromRes(hImageList, hInst, "IDI_ARROW_LEFT_48")
and don't worry about destroying the temporary icon.
This was a very nice addition. I changed my code to use this new code and now it looks much more compact and visually appealing as well as now knowing that I don't have to worry about destroying the temporary icon.
We can't think of all the problems in advance, but we have enough knowledge of the Windows API to solve them when they appear. Writing a complex application brings them to light. I did write CSED during the beta testing of PB9 and used purposely many of the new features of the beta compiler. This helped me to find many bugs that could have not discovered with trivial tests.
Working with the new resize class I realized that can't be used to resize the tab pages (modeless windows associated with the tabs), so I have added the following function:
http://www.planetsquires.com/protect/forum/index.php?topic=3838.new#new
Works nicely with tab pages created with the CTabPage class.