-
Notifications
You must be signed in to change notification settings - Fork 200
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
Improved Settings UI, consistent visibility and usability of SymbolBrowser, some refactoring #194
Conversation
commit d92d2b3 Author: Dom <[email protected]> Date: Sat Oct 8 02:57:29 2022 -0400 additional settings improvements to make them easy to read while making use of T3 UI also some style changes i neglected before commit 7ac3bf9 Author: Dom <[email protected]> Date: Fri Oct 7 22:02:43 2022 -0400 re-ordered settings to make them more readable commit 41c41d9 Author: Dom <[email protected]> Date: Fri Oct 7 21:52:48 2022 -0400 dont open the symbol browser unless the cursor is in a valid position commit 03789bd Author: Dom <[email protected]> Date: Fri Oct 7 21:37:34 2022 -0400 some settings cleanup and finishing commit d911835 Author: Dom <[email protected]> Date: Fri Oct 7 21:20:17 2022 -0400 feature complete! the symbol browser stays in view and is easy to offset commit 5f03b3c Author: Dom <[email protected]> Date: Fri Oct 7 18:18:27 2022 -0400 more style 😎 commit 57a16b5 Author: Dom <[email protected]> Date: Fri Oct 7 18:03:07 2022 -0400 some refactoring for style commit 828465b Author: Dom <[email protected]> Date: Thu Oct 6 04:23:44 2022 -0400 description panel properly stays up when in use (with an option to let it expire after X milliseconds) that option needs to be properly added to the Settings window commit 2327207 Author: Dom <[email protected]> Date: Wed Oct 5 02:42:45 2022 -0400 further steps towards keeping symbol browser inside its graph canvas commit ea1b62f Author: Dom <[email protected]> Date: Wed Oct 5 01:42:48 2022 -0400 continue refactor for better usability and maintainability commit 5bf2224 Author: Dom <[email protected]> Date: Wed Oct 5 01:42:22 2022 -0400 useful IList traversal function commit 7a85fd7 Author: Dom <[email protected]> Date: Tue Oct 4 03:51:22 2022 -0400 refactor in progress to use T3Ui may want to move DrawSettings into T3Ui as well, possibly in a separate class for "settings" specific functions (would need to move over DrawSettingsTable) commit 203d307 Author: Dom <[email protected]> Date: Sun Oct 2 14:11:24 2022 -0400 a little oopsie commit 9e1a834 Author: Dom <[email protected]> Date: Sun Oct 2 14:08:14 2022 -0400 make debug-oriented settings and stats debug-build-only, finish populating settings commit db28e4c Author: Dom <[email protected]> Date: Sun Oct 2 05:59:39 2022 -0400 table scheme, streamlined settings addition
Core/Resource/MathUtils.cs
Outdated
@@ -1,5 +1,6 @@ | |||
using System; | |||
using System.Numerics; | |||
using System.Reflection; |
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 dont know how this ended up here and I entirely blame visual studio
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 just looked at the code here on github.
Besides the definition of the settings and the async method everything looks great.
I added some comments though.
Great work with the symbol browser. But if in doubt, I would prefer smaller PRs.
public UIControlledSetting(string label, Func<string, bool> guiFunc, string tooltip = null, string additionalNotes = null, bool drawOnLeft = false, Action OnValueChanged = null) | ||
{ | ||
CleanLabel = label; | ||
_hiddenUniqueLabel = $"##{label}{_countForUniqueID--}"; | ||
_uniqueLabel = $"{label}##{_countForUniqueID--}"; | ||
|
||
_guiFunc = guiFunc; | ||
_tooltip = tooltip; | ||
_additionalNotes = additionalNotes; | ||
_OnValueChanged = OnValueChanged; | ||
DrawOnLeft = drawOnLeft; | ||
} | ||
|
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 not convinced that mixing model and view is a good idea in this case.
But it should be easy to refactor.
One note though: I personally learned to love construction with object initializers and using parameters instead of constructors:
something like...
new Settings() {
Name: "Some name",
Tooltip: "Some tooltip",
};
much easier to read and easier to maintain.
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.
im interested in your take on this, as to me this seems like a good way to enable Tooll contributors to add/remove user-facing settings with ease. i'm not deeply familiar with the model vs view concept though i think i know what you mean and share the sentiment in most cases. this case to me acts entirely as "view" - just a simple and consistent way to do so. Though with OnValueChanged
it does allow that scope to creep
I like construction with object initializers too, but I chose to go this route due to the auto-generated unique labeling system (used to prevent certain imgui bugs that can be easy to run into). You'll notice that when making use of this constructor elsewhere, I essentially treat it like an object initializer, especially due to the optional parameters. Like this:
new UIControlledSetting
(
label: "UI Scale",
tooltip: "The global scale of all rendered UI in the application",
guiFunc: (string guiLabel) => CustomComponents.DrawSingleValueEdit..... etc
),
I also want to make sure that anyone creating settings can't mess it up - they know they need at least two things (a label and a draw function), and if they have that it cant go wrong
Core/Resource/MathUtils.cs
Outdated
/// <summary> | ||
/// A simple method to get a new index by "jumping" from a startIndex | ||
/// When the jump would make the index exceed the valid range of the collection, | ||
/// it will return an index that "wraps" around the other side | ||
/// </summary> | ||
/// <param name="startIndex">Index to jump from</param> | ||
/// <param name="jumpAmount">How far to jump (usually 1 or -1 for forward and backward respectively)</param> | ||
/// <param name="collection">The collection whose Count (Length for arrays) will be referenced</param> | ||
/// <param name="realWrap">If false, this will default to making every wrap result in the first or last index of the collection. | ||
/// If true, it will be a "real" wrap, where if you jump in excess of X, you will wrap to the index X distance from the other side. </param> | ||
/// <returns></returns> | ||
public static int WrapIndex(int startIndex, int jumpAmount, System.Collections.IList collection, bool realWrap = false) | ||
{ | ||
int index = startIndex + jumpAmount; | ||
index %= collection.Count; | ||
|
||
bool newIndexIsNegative = index < 0; | ||
int negativeWrapIndex = realWrap ? collection.Count + index : collection.Count - 1; | ||
|
||
return newIndexIsNegative ? negativeWrapIndex : index; | ||
} | ||
|
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 personally think that this method is still too special to be added to Core.MathUtils.
Is it used in other places than the SymbolBrowser?
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's valid. Currently it's not, though i remember seeing another place it could have been. I can move it into the Symbol Browser if you prefer, or as an extension method for IList
DrawSearchInput(); | ||
|
||
Vector2 browserPositionInWindow; | ||
ShiftPositionToFitOnCanvas(out browserPositionInWindow, posInWindow + _browserPositionOffset, _resultListSize); |
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.
Nice solution. But why an out param instead of a Vector2 return value?
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.
just because the description of what it is doing - if it's shifting something, a return value doesn't necessarily suggest that as intuitively as passing in a reference to something that will be shifted
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 even by my own description that's not really any clearer and i should just be passing a ref
if i want to go that route
that function is under construction per your feedback and i'll keep this in mind
@@ -51,20 +52,19 @@ public void Draw() | |||
{ | |||
if (!IsOpen) | |||
{ | |||
if (!ImGui.IsWindowFocused() || !ImGui.IsKeyReleased((ImGuiKey)Key.Tab)) | |||
if (!ImGui.IsWindowFocused() || !ImGui.IsKeyReleased((ImGuiKey)Key.Tab) || !ImGui.IsWindowHovered()) |
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.
|| !ImGui.IsWindowHovered()
This sounds scary. Did you test this thoroughly for side effects?
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 did test it, but i cant say i put it through the wringer. I'm not married to this idea, it just seemed strange that if my mouse is off-screen, i can press Tab and Enter and I can find out 10 minutes later I made an Execute node way off screen
/// <param name="hoveredSymbolUi"></param> | ||
async void ExpireLastHoveredSymbolUi(SymbolUi hoveredSymbolUi) | ||
{ | ||
//if we've already started calculating this one, we can just 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.
I know it's nitpicky, but ideally comments should be with space an upper case.
This helps me to distinguish it from commented code
//if we've already started calculating this one, we can just return | |
// If we've already started calculating this one, we can just 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.
Yes ive been trying to remember this, my bad!
/// This is intended to allow the cursor to cross over the scroll bar | ||
/// of the symbol browser so examples can be interacted with | ||
/// </summary> | ||
/// <param name="hoveredSymbolUi"></param> |
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 there is nothing to say, just remove this line.
/// of the symbol browser so examples can be interacted with | ||
/// </summary> | ||
/// <param name="hoveredSymbolUi"></param> | ||
async void ExpireLastHoveredSymbolUi(SymbolUi hoveredSymbolUi) |
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.
Wow. It took me a while to understand what this method is doing.
Do we really need such a complex solution?
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 you have to feel more comfortable with immediate user interface: The method is called every frame. So it would be MUCH easier to just store the start time and the check on every frame (I.e. on every redraw) if the time has exceeded. i'm pretty sure there is no need for async 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.
yes it's very complex, I was wrestling with this for a while and I'm also not a huge fan of it. In retrospect this can very much be done in-line or non-async, but I liked the way the async method kept the timing logic and variables in one place. if this was that difficult for you of all people, i think it's definitely worth doing the other way
I can also remove the expiration-timer feature altogether - I implemented this in the case that you didnt want the symbol browser to keep the hovered-item description up indefinitely
I did some testing with the SymbolBrowser fixes and found a couple of glitches: We you want to append another op to an existing op, the layout is broken: e.g.:
Also the Layout of the symbol browser input field should be horizontally aligned with the existing node. The idea behind this is, that the search field is an "operator in the making". If roughly fills the place the new op is going to have. This is even more noticable when splitting a connection like: I'm also not sure, if flipping the description layout is a good work around. Ideally I would prefer panning the canvas to make space or even not showing the description at all. |
yeah sorry this pull request is so huge - i just ended up getting kind of scattershot. i'm new to this whole open-source-collaboration thing, and i'll be better about the size of my requests in the future for your last comment, understood! i didnt take that into account but fortunately I baked in an easy way to offset it to align correctly again. my changes are very much from the perspective of a new user, as I am one myself. personally, I always want to be able to see the descriptions, otherwise I often wont know why they're not there or if they exist at all if it's my first time using the program. But i can make that change no problem overall it seems like my changes would require a different approach and are currently at odds with the paradigm here. Since I now know the idea is for that input field to essentailly be the next node, its positioning takes priority. so there are a couple of things that can be done to keep the symbol browser usable when at the ends of the screen At the bottom of the screen, the symbol browser could show above the input field, so it remains in full view while the input field is in the correct position Or, at the bottom of the screen, the symbol browser could be shorter so that you can scroll, even though your view of the browser would be significantly limited At the right side of the screen, the symbol browser can shift to stay on screen while the input field remains where it should be As for the description panel, I can just make it disappear on the right again |
… feedback adjustments
I adjusted a lot of this to fit your feedback and to fix some bugs I think neither of us were aware of too to summarize:
|
Awesome, thanks a lot. Much better. I'm merging it on dev. |
Merged |
looking forward to seeing your approach! |
It's basically a series of function calls. But the code is several hundred lines shorter.
I also cleaned up some method names in |
It looks good! yeah I think I got too attached to my other solution with the checkbox-on-right approach, trying to make the settings look good regardless of what order they're in. in lieu of that, this approach is best. Sorry for overcomplicating an otherwise good task to onboard anyone else 😅 |
Thanks again for the contribution! I believe that one of the greatest benefits of an open source project is the code ping pong. After a couple of iterations the code always gets better, so without your contribution the settings window would not only still be untouched, but there would also be no tooltips and the custom components would still be a mess. I personally find it really hard to strike the right balance between being too careful (read: "too attached", "scared", "hesitant", "lazy") and too experimental (read: "use stuff without understanding it thoroughly", "change things because it's fun", "recklessness"). Frequently I'm too far on the "careful" side because people will complain when things break. But when too careful, we are stuck and everything fades into being outdated and obsolete. |
So I applied your feedback for the settings UI.
It's a little bit awkward imo (i still preferred the table ;))I made it a table this time around too with that final commit. let me know if the way i did it though works a little better, i just found it unpleasant to work with without thatalso made a little settings display class to make rendering lists of settings easier
Also made some significant changes to how the SymbolBrowser organizes itself - now it should never clip off of the canvas, and it should stay in view. You can now also be confident the description panel will stay up so you can scroll and pull out examples.
Also did some refactoring for maintainability and editability