-
-
Notifications
You must be signed in to change notification settings - Fork 957
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
Stride.UI styling and control structure overhaul proposal #2499
Comments
Templated elementsI love the idea but I dont fully understand how this differs from the current ModelsThis would be super handy to have OOB at least for me. I have been thinking of ways to show inventory items with a 3d view but recently settled on just having the sprite for now. I cant speak on the rendering classes with my lack of experience but I always love easily overridable functions for customization. Having a Render method the can be overridden sounds as useful as having the Draw method in EntityProcessors. |
It is mostly barrowed from AvaloniaUI, so I can't take credit. There are a few differences which I think set it apart, though most of them I didn't write out for brevity.
Yeah this would be a handy ue for it as well. Though the main use I had in mind was having the UI elements themselves be 3d. The one thing for that use-case I am not sure on is you would probably want it with a custom directional light, otherwise they would jsut be flat, or use the lighting in the current scene. So not sure how that would work. Might make sense to have another element that can handle it better. |
I'm neither for or against proposal, but would like to throw in some info/speculation into the discussion for some of the topics. UIElement.Render: .NET MAUI seems to have doubled down on this approach, though apparently subtly different to Xamarin.Forms (called Handlers now). I also think the separate Renderer/Handler allows you to override an existing control's Renderer/Handler, so you globally affect all existing controls without having to go through every UI Page (or theoretically 3rd party controls) and changing it to your derived version, which might not always be possible. Note that I'm not saying Xamarin.Forms or Maui did things right in this aspect, just pointing out the similarity. If using a Models: |
Thanks for the extra info, I didn't realize it was inspired by Xamerin.Forms, though probably should have guessed judging by the rest of the way the UI is built so similar to it.
What do you mean by "'render' the native controls" in this case?
For thise proposal, there are two cases that hard handled differently. One is if it is a
I guess, but that can happen with anything really. And something to catch during a PR review. But yeah, generally I find sealing things to be rather annoying most of the time (though there is good reason for it!). These are great points to bring up though. Thanks for brining them up and for pointing out the similarities and some of the reason why they are how they are. Models Perhapse the solution is to introduce an easier way for entity components to implement some of the input/interactive functionality of UIs. Like what you are saying. And as I'm writing this, I am starting to think this might be a better approach. And add some sort of 'viewport' for displaying entities/models in the UI (like for showing 3D previews of objects and such)?
I was just planning to use the bounds of the UI. |
Stride has some trickery/hacks with native controls. eg. For Android, when you have an In Xamarin, you basically have the Renderer which places the native textbox (per each platform) where your control is supposed to be, though this is because Xamarin was quite deep in using the native platform's controls as much as possible. Though thinking more about this, I'm not sure it's practical for Stride since it's essentially a single "canvas" independent of any platform's UI system, and also wouldn't work if the EditText is placed in the 3D world. As an aside, I feel there's a bad coupling between the native control and Stride's
Ok with me.
Ok, though with things like align left/right, stretch content, I suspect there would probably be some scaling involved (just pointing out some potential pitfalls). |
Overview
The current style and creation of controls in the UI has a number of limitations and issues which this proposal aims to solve.
This is a large proposal, and a long read, sorry in advanced!
Current system
A brief (?) overview of the current systems so we are all sort of up to speed.
UIElement
s define texture and color properties to use when they are drawn.There are
ElementRenderer
classes which handle drawing the elements.Users can create their own
ElementRenderer
class and register it with theUIRenderFeature
, either for a type, or for a specific element. To register for a type requires making an additionalIElementRendererFactory
class.UILibraries allow for configuring
UIElements
, either individually (like setting up the textures for a Slider), or groups ofUIElements
(like a group of buttons). Then use these pre-made sets of elements inUIPage
s. Like with Prefabs, when the source pre-made element changes in aUILibrary
, the changes are propagated to all their usage locations inUIPage
s.The intended workflow as I understand it is to use
UILibraries
to set up elements, including the default controls, likeSlider
, andButton
. Which you would then use in yourUIPage
instead of the default controls directly. And if you needed some special or more advanced rendering of a control. You would create anElementRenderer
for it, and then in code, register it for the element.Limitations
The current UI has a number of limitations and missing features.
Proposed changes
These are my proposed changes that address the listed limitations, and expands the flexibility of the UI system in Stride.
Styling
Add a
StyleContainer
class, and add a property for it toUIElement
.A
StyleContainer
is a collection of selector sets, with each selector set having a collection of property name and value pairs associated with it.Selectors match the
UIElement
which theStyleContainer
is defined on (only supports pseudo states), and elements that are provided by a virtualGetStylableElements()
method inUIElement
. By default no elements are returned.Selectors in a set match in order against the ancestors of an element, up to the element that defines the style. Meaning that you can do a
TypeSelector
followed by aNameSelector
, and it will only match elements with the specified name and with an ancestor of the specified type. This is much like CSS selectors if you are familiar.When a selector set matches a
UIElement
, it the values from the name-value pairs associated with the selector set will be applied to the matchedUIElement
.Reason
Allows the UI to easily support different styles for pseudo states, and controls, while not requiring defining properties for each pseudo state.
Lets controls that have internal elements still be styled in a UILibrary.
Alternatives
An alternative is to create a full CSS like stylesheet system. I decided against this option because creating a performant robust stylesheet is difficult and time consuming. And the main benefit is already covered by UILibraries, as they allow you to create reusable consistently styled elements. Where you can change the style in a central location and it is propagated to all usage locations.
So adding them would duplicate functionality and step on the toes of UILibraries. And UILibrary workflow is smoother and better suited for games than creating stylesheets anyway.
Pseudo states
Add a
PseudoStates
property toUIElement
. APseudoStates
class is a collection of the current pseudo states of theUIElement
, defined as strings. AnyUIElement
derived class can define its own pseudo states.The main use for pseudo states is for changing styling.
A
UIElement
adds or removes string pseudo states to and from thePseudoStates
class property as the element's state changes. For example, when aToggleButton
is toggled on it would add a:active
pseudo state, and remove it when toggled off.Reason
Allows any
UIElement
derived class to add any pseudo states to themselves that they need, and easily style them.Allows the implementation for the pseudo state selector and usage to be very simple and straight forward, along with updating the styling when a pseudo state is added or removed..
Alternatives
An alternative is to allow using a new
[PseudoState]
attribute onbool
andenum
properties in aUIElement
to denote them as pseudo states. And have have a selector check for the value based on the property name it is assigned.The downside of this is that the code is significantly more complex, that what is effectively, a list of strings. And requires the use of reflection to get the value of the properties to check them. Though it can be optimized, it still comes with increased code complexity, and has heavy initialization costs.
UIElement Render(..) method
Add
Render(..)
method toUIElement
, which can be overridden to handle the rendering of aUIElement
.As an example usage,
Border
would overrideRender
to handle drawing the background and border of itself.Reason
Allows rendering to be self-contained, and would make it easier to create an easier to use API for drawing/rendering.
Required for the other parts of this proposal to function.
Template elements
Add
TemplatedUIElement
andElementTemplate
classes, both inheriting fromUIElement
(names subject to change).TemplatedUIElement
would be the new default base class for control elements (Button
,Slider
, etc.). And it allows for referencing aElementTemplate
element in aUILibrary
, and instantiating it as its children when created. If no template is set, it would try to default to a template in aUILibrary
set in a new property in theGameSettings
.The
ElementTemplate
is used to define the elements and configuration of elements used by aTemplatedUIElement
and is only meant to be used as a root element inside ofUILibrary
. It has a type field for specifying the type ofTemplatedUIElement
it is intended to be used for.TempaltedUIElement
overridesGetStylableElements()
and returns all elements from the template it uses. In this way, all child elements can be styled without having to make a new template.Control elements no longer define texture properties for 'parts' (like the
Slider
hasThumb
, andTrackBackground
, etc.). Instead, they are UIElements defined in templates.Remove the current
ElementRenderer
system, as it would be redundant with the above systems.Reason
Allows for easier reuse and combining of controls to create more complex controls.
Allows for deeper customization of the look of a control without requiring the use of C#, making it easier for designers to create the UI.
Allows for adding any type of custom 'part' to the UI without having to hardcode the texture for it.
Allows supporting textures or vector UI.
Brushes
Add a
IBrush
interface, along with implementing classesSolidColorBrush
,TextureBrush
,GradientBrush
, andMaterialBrush
which can be used as properties on UIElements to define how it draws, in place of individual texture and color, properties.Reason
Is similar to C# WPF style libraries, along with design tools like Figma, so should feel familiar to both programmers and designers.
Allows 'implicitly' supporting colors, and textures, along with any custom shading the user may want, without having to explicitly add properties for them.
Follows common design pattern used in Stride.
Alternatives
Keep defining individual properties for both texture and color, and material. The downside is that it requires defining and manually handling considerably more properties. And isn't extensible if we or a user want to add some additional type of brush in the future.
Initial vector rendering.
Add support for procedural rounded corners through procedural mesh generation.
Reason
Allows the UI to immediately be able to support a range of common UI styles/designs without requiring textures.
Allows nicer UI to be mocked up faster, while not having to leave the editor to create and import textures.
Potentially lower memory footprint due to less textures.
Not difficult to support the
Depth
property ofUIElements
.Alternatives
Models
Add a
Model
property along with a toggle to some UI element that handle rendering (LikeBorder
), to support taking a 3D model and rendering that instead of using a rectangle mesh.Additional properties like either to receive shadows, receive lighting, and cast shadows, and the rotation of the model, could be added as well.
Reason
Allows the use of 3D models in the UI to create cool and unique UIs.
Allows the creating even more immersive diegetic UIs.
Expands on the existing 3D support of the UI.
Would be fairly easy to add, as the UI rendering system already supports rendering 3D geometry. (Not sure the difficulty of support lighting and shadows though).
Alternatives
BorderModel
element. Downside is that it would require the user to create new templates for every control. But does keep the existing elements 'cleaner'.ModelBrush
containingModel
,Material
, and other relevant properties. It can just be used with of anyIBrush
property. Would require handling it as a 'special case' compared to all other brushes. This is a very valid alternative imo, and might be a better option actually?Conclusion
These changes allow for artists and designers to quickly and iteratively design and style control, without the need for a C# developer. Makes it easier for developers to add new controls as they won't need to define and manage as many properties. And makes it easier to support more dynamic UI. All while working with the general workflows of Stride.
This would fully replace the current
ElementRenderer
system.It also doesn't have to be all done at once, it can be done one section at a time for the most part.
And should be noted that some of these are breaking changes, and there just isn't a way around them nicely.
Please share your thoughts and let me know if parts need clarification!
The text was updated successfully, but these errors were encountered: