-
Notifications
You must be signed in to change notification settings - Fork 696
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
Fixes #2489. Build scrolling into View. #3254
Fixes #2489. Build scrolling into View. #3254
Conversation
Oooh! I'm excited to dive into this. |
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.
Not done reviewing yet, but here's some initial thoughts.
What you're doing here is GREAT.
@@ -38,74 +37,13 @@ public class ScrollBarView : View | |||
/// </summary> | |||
public ScrollBarView () |
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 think:
- the v2 class should be named
ScrollBar
- An instance of
ScrollBar
should act 100% independently of whether there is "another scrollbar" or not. Any logic that coordinates two scrollbars should be outside of theScrollBar
class. - Logic for "auto hiding" scroll bars should NOT be in
ScrollBar
. External code should be able to just doscrollBar.Visible =
. - The public API
ScrollBar
exposes should be primarily "set these properties and subscribe to these events"; Normal use should not require overriding methods.
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 think:
- the v2 class should be named
ScrollBar
I agree and I'll do it i this PR.
- An instance of
ScrollBar
should act 100% independently of whether there is "another scrollbar" or not. Any logic that coordinates two scrollbars should be outside of theScrollBar
class.
To do this, simply start each view that wants to use separate scroll bars such as ScrollBarType = ScrollBarType.Vertical or ScrollBarType = ScrollBarType.Horizontal and manipulate what each one should do differently from the other.
- Logic for "auto hiding" scroll bars should NOT be in
ScrollBar
. External code should be able to just doscrollBar.Visible =
.
The Visible
property is actually the one that manipulates its visibility and its value is constantly changed whenever its values are resized or reset. Therefore, in my opinion it should only be used to manipulate the visibility imposed by auto-hide. But the user may not want to draw the scroll bar even if it is necessary or want to draw it even if it is not necessary. To enforce this condition, there are the AutoHideScrollBars
and ShowScrollIndicator
properties. If AutoHideScrollBars
is false
you can manipulate the visibility at will using the ShowScrollIndicator
property. That's why the Visible
property is reserved for the scroll bar so that it can be manipulated at will when the AutoHideScrollBars
property is true
. Therefore, it is not worth manipulating the Visible
property directly.
- The public API
ScrollBar
exposes should be primarily "set these properties and subscribe to these events"; Normal use should not require overriding methods.
Yes, the API should explain how to use the scroll bar in a view. Of course, TextView
and TableView
are more complex examples because they already have automatic management of these adjustments defined, as they can be used without a scroll bar.
With this PR it is now possible to use negative limits so that a view does not need to implement left and top offsets, but only provides the size of the columns and rows. See the ScrollBarBuiltIn
scenario.
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.
the v2 class should be named ScrollBar
Done.
Terminal.Gui/Views/ScrollBarView.cs
Outdated
} | ||
} | ||
|
||
// BUGBUG: v2 - Why can't we get rid of this and just use Visible? | ||
// We need this property to distinguish from Visible which will also affect the parent |
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.
The superView that has a ScrollBar
in it (which will usually be Padding
) can do
_scrollBar.VisibleChanged += ScrollBar_VisibleChanged
And update it's state as appropriate. I still don't think this property is needed.
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.
The VisibleChanged
event is really not necessary and I will remove it in this PR.
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.
The ShowScrollIndicator
is necessary as I explained before because the Visible
property is reserved for the scroll bar to handle auto-hide if it is true.
Edit:
Do you want I still maintain the comment in the line 136?
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.
The VisibleChanged event is really not necessary and I will remove it in this PR.
I only need to use the Parent.VisibleChanged
and Parent.EnabledChanged
to set the scroll bar visibility and inability if the superview has changed that properties.
Done.
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.
Ok, I got through all/most of it and played with the changes locally.
This is just fantastic stuff! It is going to really simplify the API.
I view what you've done so far as a proof-of-concept prototype that absolutely proves we can make this work elegantly.
Now, the challenge is to take all you've learned, step back, and engineer the most elegant new implementation possible.
Great work @BDisp!!!
/// <summary> | ||
/// Gets the rectangle that describes the inner area of the Adornment. The Location is always (0,0). | ||
/// </summary> | ||
public override Rect ContentArea => Thickness?.GetInside (new Rect (Point.Empty, Frame.Size)) ?? new Rect (Point.Empty, Frame.Size); |
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 am confused here.
Why is this not just Parent.Bounds
?
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.
Because the Parent.Bounds
may have negative location. Don't forget that this PR now allow using negative bounds location and thus the dimension is also changed. The ContentArea
is only the available space inside adornments used to set the driver clip area.
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'm confused.
Can you please update the API docs for Bounds
to represent what you've done 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.
I'm still working on this and I didn't decided well what I'm going to do. I'm testing the ScrollBar
on Adornments
and on ContentArea
but he behaves differently related with dimensions.
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'm still working on this and I didn't decided well what I'm going to do. I'm testing the
ScrollBar
onAdornments
and onContentArea
but he behaves differently related with dimensions.
Ok, I'm going to avoid thinking deeply about Bounds
v ContentArea
until you tell me it's time. For me, the best way to gain clarity on things like this is to write the API docs as clearly as possible. I understand you're less comfy about this, but I encourage you to give it your best shot. Happy to riff with you on it if that helps!
UICatalog/UICatalog.cs
Outdated
@@ -498,7 +498,8 @@ public UICatalogTopLevel () | |||
Title = "Categories", | |||
BorderStyle = LineStyle.Single, | |||
SuperViewRendersLineCanvas = true, | |||
Source = new ListWrapper (_categories) | |||
Source = new ListWrapper (_categories), | |||
ScrollBarType = ScrollBarType.Both |
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.
What would you say to the API looking like this:
...
Source = new ListWrapper (_categories),
HorizontalScrollBar.Enabled = true,
VerticalScrollBar.Enabled = true,
...
?
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.
Why Enabled
? This makes sense if it was deactivated before, being visible, but without the user being able to interact with it. For me, just one statement is simpler, such as CategoryList.Padding.ScrollBarType = ScrollBarType.Both;
in case we want to put it in Padding, of course.
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.
Why
Enabled
? This makes sense if it was deactivated before, being visible, but without the user being able to interact with it. For me, just one statement is simpler, such asCategoryList.Padding.ScrollBarType = ScrollBarType.Both;
in case we want to put it in Padding, of course.
Because Enabled
is perfectly suited for this, and already exists. Why add (or not remove) API surface area if there's already a primitive that is 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.
For me Enabled=true
is toggle from grayed to normal color, that why I asked. If I remember when I created that property was for that propose.
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.
Ahh... Yes. the docs say "Gets or sets a value indicating whether this can respond to user interaction."
I don't think we should change that. Your question is great.
We need two things:
- A way for a
View
subclass to say "Scrollbars make no sense for me." I was thinkingEnabled
would be good for that. It's not.
Ideas for what this mechanism could be:
- On
View
:public ScrollBar HorizontalScrollBar
- If the propertyis null
then duh. The question here is who's responsible for creating theScrollBar
instance? Lots of choices. - On
View
:public ScrollBarType ScrollBarType
- yuk. I just don't like adding more and more enums.
Other ideas?
- A way for a user of a
View
subclass (or the subclass itself) to say "make the the H or V scrollbar be visible".
This should be the Visible
property on ScrollBar
. For example _myView.HorizontalScrollBar.Visible = true
.
Terminal.Gui/View/ViewScrollBar.cs
Outdated
private int _scrollTopOffset; | ||
|
||
/// <summary>If true the vertical/horizontal scroll bars won't be showed if it's not needed.</summary> | ||
public bool ScrollAutoHideScrollBars |
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.
AutoHide
should be a property on ScrollBar
, not View
.
This will provide better encapsulation and will enable the automatic behavior work on either the horizontal or vertical (or both).
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 I'm not exposing the ScrollBar
in the View
class. That's why the property exists in the View class.
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.
Right. I get it now.
I think the name of this property should be AutoShowScrollBars
and it should be false
by default.
When it gets set to true
this logic should get executed (in Padding
, IMO):
Parent.VerticalScrollBar.Visible = Parent.ShouldTheVerticalScrollbarBeVisible();
Parent.HoriztonalScrollBar.Visible = Parent.ShouldTheHorizontalScrollbarBeVisible();;
When it gets set to false
this logic should get executed (in Padding
, IMO):
Parent.VerticalScrollBar.Visible = false;
Parent.HoriztonalScrollBar.Visible = false;
Note ShouldTheHorizontalScrollbarBeVisible
is psuedo-code for some protocol that lets Padding
ask the Parent
the question. It could be a virtual method on View
or it could be an event, or it could be a property on Padding
that the Parent
sets.
If either scrollbar has Enabled == false
these calls will (and should) do nothing. Because, by definition of Enabled
, the view effectively does not exist. This would be the case for a View
where having scrollbars does not make sense (e.g. Button
).
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 instead of Enabled=false
why not Visible=false
. Why you want to show a grayed ScrollBar
?. This is really make to much confusion to me and I have others broken code to try to fix. If a ScrollBar
doesn't make sense on a view I think is better force set on a override Visible
to false.
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 instead of
Enabled=false
why notVisible=false
. Why you want to show a grayedScrollBar
?. This is really make to much confusion to me and I have others broken code to try to fix. If aScrollBar
doesn't make sense on a view I think is better force set on a overrideVisible
to false.
I was not remembering what Enabled
did correctly. My bad. See my comment above about needing two things: A way for a View to say "I don't do scroll bars" and a way for scroll bars to be made visible/notvisible IF they're possible.
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.
Actually any view can have ScrollBar
. Let the user choose what he want.
@@ -290,14 +199,16 @@ public override bool MouseEvent (MouseEvent mouseEvent) | |||
return false; | |||
} | |||
|
|||
if (!Host.CanFocus) | |||
View host = SuperView is Adornment adornment ? adornment.Parent : SuperView; |
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.
Same comment as above. This is too tightly coupled.
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.
That said, this piece of code is central to making all this work. At some point, ScrollBar
needs to have a protocol with the view whose content is being scrolled.
What is all the information this protocol needs to convey:
- Focus (both can and has)
- The size of the content.
- The size of the visible window into the content
- ...
What else? Let's get this list flushed out and then design a concise protocol (a set of properties, methods, interfaces, etc...) that cleanly encapsulates them.
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.
Same comment as above. This is too tightly coupled.
This is not a coupling case. It is only to ensure that the Parent is focused if an adornment is clicked if the Parent can be focused.
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.
Same comment as above. This is too tightly coupled.
This is not a coupling case. It is only to ensure that the Parent is focused if an adornment is clicked if the Parent can be focused.
I don't see how you can say this is NOT a coupling case. The code above (in ScrollbarView
) has the word Adornment
in it. That is coupling.
I believe it is possible for ScrollBar
to have NO knowledge of Adornment
(other than what it inherits from View
, of course).
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 the ScrollBar
need to access the information about the position and size of the parent. Remember the ScrollBar
now will be part of the View
class.
But the fact of the ScrollBar
can be added on any view sub-class, including adornments, the latest commits broken 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.
git checkout v2_develop ./Terminal.Gui/Views/ScrollBarView.cs ./Terminal.Gui/Views/ScrollView.cs
But I also renamed ScrollBarViewTests
to ScrollBarTests
and I have various changes on both files. If I checkout them from the v2_develop
branch I'll have a bunch of conflicts, right? I only renamed because you said on a comment.
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've looked at it closely. A view referencing it's
SuperView.Bounds
is not a problem. A view that knows aboutParent.Bounds
is bad, unless it is a class derived fromAdornment
.
Many of the calculations of the ScrollBar
is based of the size of the SuperView
. Thus it need to check it to decide it need to scroll or not. With the implementations of negative bounds a ScrollBar thay is on the ContentArea
will have much more problems to calculate the size because the SuperView
bounds will have a higher value than the visible ContentArea
and the ScrollBar
must only use the visible ContentArea
size. But because the ScrollBar
dimensions use Dim()
or Dim(1)
it will overflow the visible ContentArea
size. I hope you can understand this and you may some better suggestion. With Adornments
this isn't a problem because they aren't use negative bounds.
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.
Now I'm thinking that it was the ScrollView that should be renamed ScrollBar and not the ScrollBarView. I will reverse this when this PR is more solid.
See the ScrollBars
scenari which have ScrollBar
on all Adornments
and ContentArea
with mouse handling. For negative bounds we have to use Pos/Dim
absolute values to position the ScrollBar
. I hope you understand why I need to access to the SuperView
.
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.
Many of the calculations of the ScrollBar is based of the size of the SuperView.
No, this is not strictly true.
The right statement is:
Nany of the calculations of the ScrollBar need to use information known by the SuperView when the ScrollBar is used w/in
Padding
."
In an earlier post I listed exactly what those things (there are 7 of them...unless I'm missing something, which is possible) are.
- The position and dimensions of the
ScrollBar
:ScrollBar.X/Y/Height/Width
. - The size of the virtual content:
ScrollBar.Size
- The position w/in the virtual content that scrollbar is pointing to:
ScrollBar.Position
- The size of the visible area:
Parent.Bounds.Size
. - The offset into the virtual content that the (0,0) of the visible area resides at.
Parent.Bounds.Location
- Whether the parent has focus or not.
SuperView.HasFocus
- The ColorScheme of the parent.
SuperView.ColorScheme
These are just data. They can be provided to ScrollBar
in many different ways. One way is by having ScrollBar
access SuperView
, which you keep insisting is the only way.
The way I think will make the new ScrollBar
simpler and more flexible is to ensure ScrollBar
supports an API (a set of properties/methods) that let those things be known to ScrollBar
.
1, 2, and 3 are already handled this way. 6 and 7 can still work via SuperView
because that's how most other views work for focus and colorschemes.
For 4: You could add a public Size VisibleArea
to ScrollBar
.
For 5: You could add a public Point ContentOffset
to ScrollBar
.
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 already reverted the rename of the ScrollBarView
and ScrollBarViewTests
.
…n.Top if Application.Begin was called." This reverts commit 85e1e6c.
I'm reopen this because I'm convinced that the WindowsTerminal_ECSMSxAgT4.mp4 |
Yes you are right. That was because
You are right. I didn't worry about that. The only point I wanted to transmit was the offsetting is possible during the layout, instead of using always the location (0,0) and then doing the scrolling after.
Yes and I really appreciated your work, but in my opinion I think you underestimated the negative location. It's possible to handle with Positive and Negative
Well that isn't completely right. This PR has code for scrollbars and without scrollbars handling as well. Please see better.
Well if you are talking about I'm using negative location, I think that is the right approach, unless the others API are also wrong.
The use the
Before you are considered using negative bounds and now you are completely against. But now I'm not talking about negative location. Now I'll talking about positive offset. Imagine you want to draw the
As I said above the
You see here. So it's always equal or greater than zero but always starting at (0,0) of the
I agree.
Well it seems you are killing for good the use of scrollbars. Please don't use them on your applications tools, like the VS2022. It's bad.
For me a page size is always the visible content area size. This is very useful for users navigate faster horizontal and vertical for each visible content area and thus never loose anything on viewing them. |
…out-location_3362
…bar-on-padding_2489
Some more improvements. WindowsTerminal_MGm6mXDBDO.mp4WindowsTerminal_Jl0v1eUPWC.mp4 |
I am still unsure why you are spending time continuing to work on this instead of helping get #3323 right/done. |
I'm just trying to prove that the layout supports negative locations. I'm not trying to impose anything or ask for anything. Pretend this doesn't exist. I'm busy with other things but of course I will collaborate on #3323. Comment in #3323 what you specifically want me to collaborate on and I'll try to help as best I can. |
Fixes
ScrollBar
based on a newScroll
and removeScrollBarView/ScrollView
#2489Proposed Changes/Todos
Pull Request checklist:
CTRL-K-D
to automatically reformat your files before committing.dotnet test
before commit///
style comments)