New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Removed changes of PR #3833, suspend/resume redrawn of ToolStripEx when dropdown menu is opened/closed #10260
Conversation
I think that CodeFactor issue at GitUI\CommandsDialogs\FormBrowse.cs:2952-2960 is unrelated, but let me know if I can do something. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
Seem to work for me.
One comment on code removed in toolbar.
The core review is for the added code in ToolStripEx.cs, more reviews needed there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please add animations clearly showing the old and the new behaviours?
GitUI/UserControls/ToolStripEx.cs
Outdated
[DllImport("user32.dll")] | ||
private static extern int SendMessage(IntPtr hwnd, int wmsg, bool wparam, int lparam); | ||
|
||
private const int WM_SETREDRAW = 11; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All interop code resides in https://github.com/gitextensions/gitextensions/tree/master/GitUI/Interops. We already have definitions for SendMessageW
:
public static extern IntPtr SendMessageW( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added NativeMethods.WM_SETREDRAW
with XML documentation taken from MSDN and slightly modified.
I also added NativeMethods.TRUE
and NativeMethods.FALSE
, I wonder why they are not already present, let me know if you see a better way.
GitUI/UserControls/ToolStripEx.cs
Outdated
private static void SuspendDrawing(Control control) | ||
{ | ||
if (control != null) | ||
{ | ||
SendMessage(control.Handle, WM_SETREDRAW, false, 0); | ||
} | ||
} | ||
|
||
private static void ResumeDrawing(Control control) | ||
{ | ||
if (control != null) | ||
{ | ||
SendMessage(control.Handle, WM_SETREDRAW, true, 0); | ||
control.Refresh(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two methods have single call site each, and should be inlined. There's no need to create more stack frames.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand you point of view, but since source code is for humans and not for computers, I think it is better to have short and well named methods than saving a stack frame.
Let me know if this is a blocker for you and I will change as requested, but I'd like to make sure before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but since source code is for humans and not for computers,
There are many different schools of thought and opinions on the subject, but small methods which are only called from one place impose maintenance and mental tax too. (Not to mention potential have perf implications).
For the sake of discussion, suppose, I follow your argument, let's compare the proposed code:
private static void SuspendDrawing(Control control)
{
if (control != null)
{
NativeMethods.SendMessageW(control.Handle, NativeMethods.WM_SETREDRAW, NativeMethods.FALSE, IntPtr.Zero);
}
}
private void SplitButton_DropDownOpening(object? sender, EventArgs e)
{
if (sender is ToolStripDropDownItem item)
{
SuspendDrawing(item.Owner);
}
}
...and an alternative:
private void SplitButton_DropDownOpening(object? sender, EventArgs e)
{
if (sender is ToolStripDropDownItem item && item.Owner is Control control)
{
NativeMethods.SendMessageW(control.Handle, NativeMethods.WM_SETREDRAW, NativeMethods.FALSE, IntPtr.Zero);
}
}
Could you please articulate why the first snippet is better? It's more verbose, it has multiple if
conditions and, thus, more complex.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is more verbose if you take it in its whole, but let's just take the entry points:
private void SplitButton_DropDownOpening(object? sender, EventArgs e)
{
if (sender is ToolStripDropDownItem item)
{
SuspendDrawing(item.Owner);
}
}
It reads like a book, reading this I know that entering the if
, it will suspend redraws, and most of the time that's all I need to know. If I choose or need to know how redraws are suspended, I can decide to visit the SuspendDrawing()
method. I can also decide to not spend time for that and move on. Also this entry point code is shorter than the second snippet.
With the follwoing:
private void SplitButton_DropDownOpening(object? sender, EventArgs e)
{
if (sender is ToolStripDropDownItem item && item.Owner is Control control)
{
NativeMethods.SendMessageW(control.Handle, NativeMethods.WM_SETREDRAW, NativeMethods.FALSE, IntPtr.Zero);
}
}
First, the if
condition is longer and more complex, so it's harder, when reading the code quickly, to get a grasp of how and when the if
will enter.
Second, the statement in the if
barely tells what it does by itself, and one will probably have to search MSDN or else to understand what it really does. You could add a comment, but then that will make the code longer and therefore more verbose. Also, comment may go desync the day one decide to send another type of message for some reason but forget to update the comment. You may argue that it's also possible to change the message and forget to rename the method (in the case of first snippet), but it's less likely to happen when the method does just one single thing. I believe code beats comments, so I prefer to have a short and well named method that's self-describing, than obscure code commented somewhere.
One more thing, the day someone else needs that feature too, the code is already factorized (partly) and ready to use. With the second snippet, it's easy to move the content of the if
and forget about also taking the if
for null check. With the first snippet, moving the method to a dedicated class (could be moved to NativeMethods
) is all that's to be done as it is already self-sufficient and self-describing.
As for performance, it is also a very important topic for me and I agree with you, but here we are not in a hot path at all, so trying to save a stack frame is not really something that should drive this decision, IMHO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the explanation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I inlined the methods.
GitUI/UserControls/ToolStripEx.cs
Outdated
|
||
private void SplitButton_DropDownOpening(object? sender, EventArgs e) | ||
{ | ||
if (sender is ToolStripDropDownItem item) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (sender is ToolStripDropDownItem item) | |
if (sender is ToolStripDropDownItem item && item.Owner is not null) |
ditto below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's assume we keep SuspendDrawing()
and ResumeDrawing()
not inlined (cf comment above), and since the first thing they do is to check for null
, would this change still be necessary ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this change still be necessary?
only if the functions are merged (I tend to keep them separate but YAGNI.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this check since methods have been inlined.
Here is a video with the current fix. gitextensions-issue10254-3.mp4To compare with previous fixes and previous state, have a look at videos posted in #10254 Sorry for the huge black strips, not an expert in video edition :/ |
…oolStripEx when dropdown menu is opened/closed
94917c5
to
a739ace
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fine for me with RussKie changesto be decided on (I would like to see some comments at least and wish there were an inline
in C#, not just advisory, even if I expect that the compiler should be able to inline automatically here).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
have not run yet
GitUI/UserControls/ToolStripEx.cs
Outdated
|
||
private static void SuspendDrawing(Control control) | ||
{ | ||
if (control != null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (control != null) | |
if (control is not null) |
is our coding style.
(If there was more code, rather:
if (control != null) | |
if (control is null) | |
{ | |
return; | |
} |
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Methods are gone (inlined) so this code is gone too.
GitUI/UserControls/ToolStripEx.cs
Outdated
|
||
private static void ResumeDrawing(Control control) | ||
{ | ||
if (control != null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
GitUI/UserControls/ToolStripEx.cs
Outdated
|
||
private void SplitButton_DropDownOpening(object? sender, EventArgs e) | ||
{ | ||
if (sender is ToolStripDropDownItem item) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this change still be necessary?
only if the functions are merged (I tend to keep them separate but YAGNI.)
The reasons for the verbosity explained, and I'll agree to disagree.
bd6abc9
to
2386ed2
Compare
Based on @RussKie disagreement, I inlined the calls and added the proper |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Include in 4.0
Merging tomorrow |
Thank you
|
This PR does:
ToolTipEx
control when a child element of typeToolStripDropDownItem
opens/closes a dropdown menusSee related issue #10254 (recent) and #3832 (old).
Previous PR as an attempt to fix this problem is #3833. It used to work but the fix was not satisfying (non-sense and dirty hack) and stopped being effective after a few versions of GE.
Also, see #10259 since this is my first PR from after 23 January 2019.