vchelaru / Gum

Flexible layout tool for creating UI on any platform
http://gumui.net/
MIT License
149 stars 32 forks source link

Discussion about Gum global variables #103

Open vchelaru opened 2 years ago

vchelaru commented 2 years ago

Problem

Applications often use variables which should be consistent across multiple UI instances or even multiple UI types (such as colors that might be used in different components). Currently this can be handled through states, but each type of object must have its own state. If a variable needs to change, then every state that references this variable needs to be found and updated. Also, this requires management of states on objects which may never change their states.

Inheritance and states do provide some support for changing variables in one place and having them apply in many others, but it is limiting and can be tedious on larger projects.

Proposal

To solve this problem, Gum needs to introduce a few new concepts:

  1. A Global Variables object which can have new variables added to it.
  2. The ability to add new variables to Screens and Components. While this is not technically necessary to solve this problem, this will likely help solve the problem.
  3. A reference system where one variable can reference another variable.

Global Variables

Global Variables is a collection of variables which can be edited just like any other object (such as an instance). Global Variables will appear in its own tab and display a property grid. Variables can be added and removed from Global Variables. If a variable is added, its name and type must be selected, and the right type of UI will be shown for variables. For example, string variables will show a text box, bool variables will show a check box, and unit variables (such as WidthUnits) will show a collection of buttons for selecting the WidthUnit.

The appearance will be just like the current property grid:

image

These variables will be saved in their own XML file, just like a separate Component or Screen.

Question - should GlobalVariables support states? This would allow grouping of variables in states. For example, an Gum project may create one state per platform (desktop, mobile, Xbox), which can impact how objects appear. Then a state can be selected in Gum (or in code) and all components and screens will react to that state changed. Vic thinks that this would be useful, and multiple users have asked for similar concepts.

Note that global variables can also reference other variables. See variable referencing below.

Screens/Components adding new variables

A Screen or Component should be able to add a new variable. For example, a Screen may want to have a variable like Spacing which can be used to space all contained elements. Initially, adding a new variable does not have any affect on the contained instances, but new variables can be used in combination with variable references to create powerful "automatic" behavior. See more info on variable references below.

Variable References

This feature allows variables to reference other variables using a special syntax. The exact syntax is open for discussion, but it should probably mimic some existing syntax. For example, we may want to use the same syntax as Excel/Google Sheets. I will assume this syntax for explaining how the variable works.

Normally, variables contain constant values:

image

A variable reference allows referencing another object in the screen. For example, variable references could be used to position a Button object so its X value always equals the X of TextInstance. In this case, the syntax would be:

=TextInstance.X

image

Pressing ENTER or tabbing out of the text box will evaluate the formula. The formula will be displayed in the text box so it can be modified, and the resulting value will appear as a read-only note.

image

Reference code (aside from the special character such as '=') uses the same syntax as C#. In other words, TextInstance.X references the object named TextInstance which lives inside the current Screen/Component, and the period is used to access properties on the object.

This syntax allows mathematical operations, allowing variables to be modified by constants such as:

=TextInstance.X * 2

Furthermore, multiple variables can be combined in formulas:

=TextInstance.Width + UnitTextInstance.Width

GlobalVariables is a reserved keyword, allowing variables to access global variables. For example, a Text object's FontSize could be:

=GlobalVariables.ParagraphFontSize

Variables can use references in the default state as well as categorized states. Therefore, a Text object could have different states such as Paragraph, H1, H2, and each one could reference different variables. Variables in one state can be references, while variables in another state can be constants.

Variables on instances can reference variables on the container itself. The this keyword allows an instance to reference its container. For example, instances in a list could be spaced by a single variable at the Screen container. Assuming the Screen has a variable called ItemSpacing then each item could set its Y in a stacked container:

=this.ItemSpacing

Variables which are referenced can themselves be references to other variables. Chain of references allow powerful cascading behaviors similar to ViewModel dependencies in MVVM.

Further Enhancements

This feature lays the foundation for a very powerful system which can be used to provide lots of other functionality. The following lists future improvements which could be built off of these features.

Additional Keywords

Additional keywords (probably prefixed with some special character) will allow for common references. For example, an text object may want to adjust its font so that it is slightly smaller than the parent text:

=$parent.FontSize

Conditional Assignments

Conditional assignments could use the C# ternary operator. For example, if a Screen defines "SmallIcons" boolean, then icons could have the following to set their width:

=this.SmallIcons ? 25 : 50

Furtermore, additional references could be performed in the conditions:

=ShowLargeFont ? GlobalVariables.LargeFont : GlobalVariables.SmallFont

Formulas

Formulas like in Excel could be used to support more powerful variables. For example, an object could match its parents width, but never be larger than 500 pixels:

=Min( Container.Width, 500)

Helper Variables

Additional variables may be useful for creating powerful variables. Natively, objects have X, Y, Width, Height, but those are relative to units. Additional variables which would be useful in formulas are:

New Unit Types

Gum provides a collection of "units" which effectively perform formulas on the value.

image

Additional custom unit types may be useful for users to define. Unit types can be defined and would have the following properties:

vchelaru commented 2 years ago

This is a really really big feature, and I think it's best to dice this up into smaller parts. I'm proposing an initial set of functionality:

  1. Add the ability to reference single variables = Owner.VariableName
  2. Add the ability to create new variables in components and screens

This reduces the features to the minimum amount needed to get something functional.

vchelaru commented 2 years ago

Update - variable references now exist. This is the first step and it enables the central definition of styling. This should be used in the Gum template version 3 before moving forward with anything else.