Icon support for all columns in Winforms Table#4164
Icon support for all columns in Winforms Table#4164freakboy3742 merged 15 commits intobeeware:mainfrom
Conversation
|
The CI log is showing a native crash, so there must be something wrong with the way you've used the native APIs. I'll switch this PR to draft status; once all the tests are passing, please post a comment and we'll take a look at it. |
|
@mhsmith The tests are passing now. |
|
Thanks, but it looks like you've done some kind of a rebase which has caused this PR to include all of #4161 as well. We prefer not to rebase PRs after someone else has already seen them, as it makes it difficult to keep track of changes. Merging from the main branch will probably resolve the situation. @rkm and me will be out of office for the next week and a half, so reviews may take longer than normal. Any other team member is welcome to pick this PR up. |
1f93398 to
465c0da
Compare
johnzhou721
left a comment
There was a problem hiding this comment.
Welcome to Toga, BeeWare, and open-source development in general! It's quite exciting to see very advanced code contributions like this, especially as a first-timer. I'm impressed by your sheer Win32 knowledge.
[Disclaimer: I'm not part of the core team.]
I'm not as familiar as the internals of Windows as you are (by the way: #2574 may be something you're interested in if you're this into Windows development) — and I haven't had the time to actually test this, but I do have a few suggestions.
| def _subclass_proc( | ||
| self, | ||
| hWnd: int, | ||
| uMsg: int, | ||
| wParam: int, | ||
| lParam: int, | ||
| uIdSubclass: int, | ||
| dwRefData: int, | ||
| ) -> LRESULT: | ||
| # Remove the window subclass in the way recommended by Raymond Chen here: | ||
| # https://devblogs.microsoft.com/oldnewthing/20031111-00/?p=41883 | ||
| if uMsg == WM_NCDESTROY: | ||
| RemoveWindowSubclass(hWnd, self.pfn_subclass, uIdSubclass) | ||
|
|
||
| if uMsg == WM_REFLECT_NOTIFY: | ||
| phdr = cast(lParam, POINTER(NMHDR)).contents | ||
| code = phdr.code | ||
| if hex(code) == hex(LVN_GETDISPINFOW): | ||
| disp_info = cast(lParam, POINTER(NMLVDISPINFOW)).contents | ||
| self._set_subitem_icon(disp_info.item) | ||
|
|
||
| # Call the original window procedure | ||
| return DefSubclassProc(HWND(hWnd), UINT(uMsg), LPARAM(wParam), LPARAM(lParam)) |
There was a problem hiding this comment.
As suggested on line 18, this may be worth moving to the libs module you create if manually doing this helper is warranted.
There was a problem hiding this comment.
I think it's better to leave this here. I think that window procedure callback functions are usually defined in the classes that they are used. Also, here the messages LVN_GETDISPINFOW are specific to a ListView object in virtual mode.
| ) | ||
| self.add_action_events() | ||
|
|
||
| def _enable_multi_icon_style(self): |
There was a problem hiding this comment.
This function is only called once. Would it be too tedious to inline it into where this is called?
P.S. same applies for _set_subitem_icon
|
Thanks for the review and suggestions @johnzhou721 ! I'll look over them and make some changes |
|
@mhsmith I think you have the wrong user 🙂 |
Co-authored-by: John <johnzhou721@gmail.com>
|
@Oliver-Leigh FYI -- by this point I'm satisfied with the overall structures of the things you've done. Feel free to ask the core team for a review [once tests are passing]. |
freakboy3742
left a comment
There was a problem hiding this comment.
This is definitely testing the limits of my Win32 API knowledge, but I can't argue with success - and you've evidently picked up some edge case bugs in TextInput as well. Thanks for the PR! I've pushed one minor tweak to the changenote, and removed the documentation
If you're sufficiently familiar with Win32 APIs to get this working, could I tempt you to fill the one big gap in our Winforms backend? The Tree widget is the one notable widget we're missing on Winforms - we've held off because the "out of the box" Winforms tree doesn't allow multiple columns of data. I've been led to believe that this might be fixable with some Win32 magic, but that's well outside my comfort zone. If this is an area where you have expertise, a contribution would be hugely appreciated.
| @@ -0,0 +1 @@ | |||
| The Table widget on WinForms can now display icons in all columns. Previously, icons were only supported in the first column. Changes to the tests have also been made to test the new structure. | |||
There was a problem hiding this comment.
| The Table widget on WinForms can now display icons in all columns. Previously, icons were only supported in the first column. Changes to the tests have also been made to test the new structure. | |
| The Table widget on WinForms can now display icons in all columns. Previously, icons were only supported in the first column. |
It definitely looks possible! I’m not sure whether it can be a modified .NET approach (like here), or whether it has to be a full blown Win32 approach. I have a Win32 base proof of concept for the detailed list widget which I’ll be posting shortly. I’ll look into the tree widget once that’s done. |
|
@Oliver-Leigh Awesome news - I look forward to seeing both widgets :-) I'll freely admit that Windows support is an area where we (as a project) aren't strong. John has already mentioned #2574 and the WinUI3 backend; see #4204 for a summary of the bigger picture issues we're facing. Any expertise you've got in the area that you're willing to volunteer is gratefully accepted. |
Previously, the Windows version of the
Tablewidget only supported icons in the first column. This update extends the functionality by allowing icons in all columns.The functionality works as follows:
LVS_EX_SUBITEMIMAGES(detailed at https://learn.microsoft.com/en-us/windows/win32/controls/extended-list-view-styles)ListViewobject is in virtual mode, so the icons are set by intercepting and handling theLVN_GETDISPINFOmessage. (The standard way of handling these messages in C++ is detailed athttps://learn.microsoft.com/en-us/windows/win32/controls/use-virtual-list-view-controls)
LVN_GETDISPINFOmessages are intercepted by subclassing theListViewobject with theSetWindowSubclassfunction.Tableentries are set using a new function_set_subitem_icon.Tableentries utilises the existing .NET implementation with theRetrieveVirtualItemfunction.(ListViewItem, icon_indices)whereicon_indicesis a tuple containing the indices if the associated icons in the WinFormsListViewicon list (-1is used to indicate there is no icon).PR Checklist: