Skip to content
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

Merged
merged 5 commits into from Oct 24, 2022

Conversation

TanukiSharp
Copy link
Contributor

@TanukiSharp TanukiSharp commented Oct 15, 2022

This PR does:

See 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.

@ghost ghost assigned TanukiSharp Oct 15, 2022
@TanukiSharp
Copy link
Contributor Author

I think that CodeFactor issue at GitUI\CommandsDialogs\FormBrowse.cs:2952-2960 is unrelated, but let me know if I can do something.

Copy link
Member

@gerhardol gerhardol left a 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.

Copy link
Member

@RussKie RussKie left a 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?

Comment on lines 42 to 45
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hwnd, int wmsg, bool wparam, int lparam);

private const int WM_SETREDRAW = 11;
Copy link
Member

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(

Copy link
Contributor Author

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.

Comment on lines 47 to 57
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();
}
}
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I inlined the methods.


private void SplitButton_DropDownOpening(object? sender, EventArgs e)
{
if (sender is ToolStripDropDownItem item)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (sender is ToolStripDropDownItem item)
if (sender is ToolStripDropDownItem item && item.Owner is not null)

ditto below

Copy link
Contributor Author

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 ?

Copy link
Member

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.)

Copy link
Contributor Author

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.

@ghost ghost added the 📭 needs: author feedback More info/confirmation awaited from OP; issues typically get closed after 30 days of inactivity label Oct 16, 2022
@gerhardol gerhardol added this to the 4.0.0 milestone Oct 16, 2022
@ghost ghost removed the 📭 needs: author feedback More info/confirmation awaited from OP; issues typically get closed after 30 days of inactivity label Oct 18, 2022
@TanukiSharp
Copy link
Contributor Author

@RussKie

Could you please add animations clearly showing the old and the new behaviours?

Here is a video with the current fix.

gitextensions-issue10254-3.mp4

To 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 :/

@RussKie RussKie added the 📭 needs: author feedback More info/confirmation awaited from OP; issues typically get closed after 30 days of inactivity label Oct 18, 2022
Copy link
Member

@gerhardol gerhardol left a 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).

@ghost ghost removed the 📭 needs: author feedback More info/confirmation awaited from OP; issues typically get closed after 30 days of inactivity label Oct 18, 2022
Copy link
Member

@mstv mstv left a 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


private static void SuspendDrawing(Control control)
{
if (control != null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (control != null)
if (control is not null)

is our coding style.

(If there was more code, rather:

Suggested change
if (control != null)
if (control is null)
{
return;
}

)

Copy link
Contributor Author

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.


private static void ResumeDrawing(Control control)
{
if (control != null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto


private void SplitButton_DropDownOpening(object? sender, EventArgs e)
{
if (sender is ToolStripDropDownItem item)
Copy link
Member

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.)

@RussKie RussKie dismissed their stale review October 21, 2022 23:24

The reasons for the verbosity explained, and I'll agree to disagree.

@TanukiSharp
Copy link
Contributor Author

TanukiSharp commented Oct 22, 2022

Based on @RussKie disagreement, I inlined the calls and added the proper item.Owner check. Also added comments. I think this should address all the remaining issues, but let me know if there's something else.

Copy link
Member

@gerhardol gerhardol left a 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

@gerhardol
Copy link
Member

Merging tomorrow

@gerhardol gerhardol merged commit 8cfd4a9 into gitextensions:master Oct 24, 2022
@ghost ghost modified the milestones: 4.0.0, vNext Oct 24, 2022
@gerhardol gerhardol modified the milestones: vNext, 4.0.0 Oct 24, 2022
gerhardol pushed a commit that referenced this pull request Oct 24, 2022
…en dropdown menu is opened/closed (#10260)

(cherry picked from commit 8cfd4a9)
@TanukiSharp TanukiSharp deleted the fix-10254 branch October 24, 2022 22:50
@RussKie
Copy link
Member

RussKie commented Oct 25, 2022 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants