Skin editor proposal - long post
Posted: Thu May 03, 2007 16:46
This post proposes a design for a skin editor for the CEGUI system. Please read it as a list of suggestions and ideas, rather than a formal design document. Such a writing can be a next step after a discussion in this thread. Note that I have not done any coding yet, not even prototyping. So this post is not limited by any technical knowledge yet
Abstract
For a first version it is suggested to be able to edit and create files based on the current Falagard C++ setup, as can be found in the ‘WindowRenderSets’ directory. By looking in that directory, a hard-coded list of editable widget types can be created.
Communication with CEGUI
We can use (instantiate) the Falagard classes and use setters and getters, so that we actually build the looknfeel within CEGUI itself. This has (like the Layout- and Imageset editors) the advantage or having an actual CEGUI preview. Besides that, the falagard objects are able to serialize themselves to a .looknfeel file with the ‘writeXMLToStream’ method, which is rather powerful. We can use the knowlegde of the Falagard.XSD file within this utility, without actually reading that file itself.
Most logic has to be programmed anyway, and besides that the XSD file is not subject to modifications that likely. But we might look into (existing) utilities to represent an XSD file in memory with easy query functionality. That would reduce some copying of XSD ‘stuff’ to code.
Limitations
With this editor it is not possible (yet) to define new widgets, such as a Tree or an Animation to name a few. As mentioned in the abstract, users can only edit widgets which have a current C++ counterpart.
Overview
Based on initial research the following image shows a rough overview of a possible application:
Creating a new looknfeel
When adding a looknfeel, the user is prompted for a name for it, such as “MyLook”. From now on, each added widget will automatically use this name + “/”.as its own name. As soon as a new looknfeel is created, an array of buttons will be added to a panel. By clicking such a button, the corresponding widget can be edited.
Loading an existing looknfeel
To load a file, we can pass it to the ‘WidgetLookManager::parseLookNFeelSpecification’ method, and then query it using a (new) ‘WidgetLookManager::getWidgetLookIterator’ method. From that query we can add a button array (see previous paragraph) with all the available widgets. Widget types which are not available yet can be marked in red or something.
Saving a looknfeel
The C++ call ‘writeXmlToStream’ can be used to serialize the current setup to a .looknfeel file.
Editing a widget
My first idea was to show a tree representing the looknfeel, and be able to edit settings via a grid. However a setting is very often more then just a Name, Value pair. For example a TextComponent defines an Area which is a componentAreaType. This block defines a choice of dimension types. Next, those choices themselves might contain sequences. This does not fit within a grid, and in order to avoid a very complex nested system of editing panels, a different apprauch was come up with…
Rich XML
Another option is to use a kind of “rich” XML edit panel, which is a read-only panel, but accepts user input through double mouse clicks and line browsing through the arrow keys. These mouse clicks will invoke helper dialogs, which will assure a safe -read: less error prone- way of editing the XML. However the user can still see what’s going on ‘under water’. Maybe there should be a ‘hardcore’ option which allows users to manually edit the XML for whatever reason. This panel will show the looknfeel XML contents of the currently edited widget (not the entire stream).
This panel has the following properties:
*Read-only (not mandatory).
*Coloured values (text between “’s), which clearly show that they can be modified.
*Ability to double-click these coloured texts in order to edit them. Editing is context sensitive; when the value is a ‘free’ value, an input dialog box is opened. When the text belongs to a choice-field (‘true/false’ or ‘add/subtract/multiply/divide’ or such) double-clicking will result in an option list dialog box. Choices are explained in detail in the “Choices” paragraph.
*A smart way to decide in which definition or block the cursor is currently residing. We can use the information from the XSD file in order to do this.
*The ability to add elements or blocks, based on the current position in the definition.
XML Editing Example
(note that i had coloured all values but this seems not to work on the forum...) This chapter will hopefully make clear –through a sample- how I see the XML editing panel work. For this example we start with a new looknfeel names “MyLook” and we start by editing the Button, which we initially call MyButton.
Now based on the Falagard.xsd file we determine what we may add, and come up with an initial XML content. Rule 1: When an element is optional (‘minocurs=0’), it will be added as a ‘Double-click here to add this’ line.
Now we add a property definition by clicking on the first line. The XML changes to:
Rule 2: we will use the default values for element attributes as found in the .XSD file. Rule 3: when an added line is part of a sequence, we add the option for another line of the same type.
Let’s now add an Imagery Section for the ‘hover’ state of the button. This is a bit more advanced:
Step 1: double-clicking the Imagery Section expands with the optional sub-tags.
Step 2: double-clicking the ImageryComponent Section expands too. Note that required options (such as the Area) are automatically added and don’t need be double-clicked in order to create, since that would invoke a needless extra step.
We double-click the dimension choice (see paragraph on choices), and choose the ‘dimensionType’ instead of the ‘settingByPropertyType’ option (both found in the XSD). This will automatically expand 4 elements with their default values. Since the dimension type’s attribute (dimensionTypeEnum) is also a choice value by it selves, we default to the first option which is ‘LeftEdge’. I consider this defaulting as ‘Rule 4’.
And so on…
We can–for extra robustness- validate the current XML contents against the XSD file after a modification and present the user with warnings and errors. However -because of the readonly-ness of the panel- this should only occur in case of bugs in the tool’s editing capabilities. So it’s a good for testing purposes and bug hunting.
Choice and restrictions
Each “Double click to choose …” line corresponds to an input dialog which requests the user to make a choice. The following choice types have the following options, which will just be shown as strings to the user:
*ComponentAreaType: { Dim(dimensionType), AreaProperty(settingByPropertyType)}
*Dimension type: { UnifiedDim (unifiedDimType), AbsoluteDim (absoluteDimType), ImageDim(imageDimType), WidgetDim(widgetDimType), FontDim(fontDimType), propertyDim(propertyDimType)}
*Colour type: { Colour(colourType), Colours(colourRectType), ColourProperty(settingByPropertyType), ColourRectProperty(settingByPropertyType)}
*Image type : { Image(imageType), ImageProperty(settingByPropertyType)}
*Horizontal Format type: { HorzFormat (horzFormatType), HorzFormatProperty (settingByPropertyType)}
*Vertical Format type: { VertFormat(vertFormatType), VertFormatProperty(settingByPropertyType)}[/list]
After selecting an option, the editor will replace the XML line to an actual tag or block. For example when choosing ‘HorzFormat (horzFormatType)’ from the Horizontal Format type choice, this XML line will be inserted:
Again note the default value (LeftAligned) for the ‘type’ attribute’s choice(horzFormatEnum).
Besides choices, there are also limitations such as enumeration values. This list is not pasted in here, because it’s quite big and very readable in XSD format. Just open the Falard.xsd file and look at this line and below:
Discussion
Besides (of course) everything written above, there are some remaining points of discussion:
*When attributes are optional, such as ‘RedrawOnWrite’ in a PropertyDefinition, should we write them or not? The example shows then sometimes, just based on an existing (WindowsLook) file. Optional attributes could be marked in a different colour.
* …
Required CEGUI code changes
Probably independent of the actual implementation we choose, some changes are required to CEGUI itself in order to help querying the Falagard system a little better:
*Some Falagard classes should be enhanced with iterators on their privately defined elements. For example a ‘getImagerySectionIterator()’ in the ‘WidgetLookFeel’ class. And some more of those.
* …
Conclusion
You have just read my initial thought written down during a short holiday. Please comment on it, ask questions and so on. Thanks for your time!
Abstract
For a first version it is suggested to be able to edit and create files based on the current Falagard C++ setup, as can be found in the ‘WindowRenderSets’ directory. By looking in that directory, a hard-coded list of editable widget types can be created.
Communication with CEGUI
We can use (instantiate) the Falagard classes and use setters and getters, so that we actually build the looknfeel within CEGUI itself. This has (like the Layout- and Imageset editors) the advantage or having an actual CEGUI preview. Besides that, the falagard objects are able to serialize themselves to a .looknfeel file with the ‘writeXMLToStream’ method, which is rather powerful. We can use the knowlegde of the Falagard.XSD file within this utility, without actually reading that file itself.
Most logic has to be programmed anyway, and besides that the XSD file is not subject to modifications that likely. But we might look into (existing) utilities to represent an XSD file in memory with easy query functionality. That would reduce some copying of XSD ‘stuff’ to code.
Limitations
With this editor it is not possible (yet) to define new widgets, such as a Tree or an Animation to name a few. As mentioned in the abstract, users can only edit widgets which have a current C++ counterpart.
Overview
Based on initial research the following image shows a rough overview of a possible application:
Creating a new looknfeel
When adding a looknfeel, the user is prompted for a name for it, such as “MyLook”. From now on, each added widget will automatically use this name + “/”.as its own name. As soon as a new looknfeel is created, an array of buttons will be added to a panel. By clicking such a button, the corresponding widget can be edited.
Loading an existing looknfeel
To load a file, we can pass it to the ‘WidgetLookManager::parseLookNFeelSpecification’ method, and then query it using a (new) ‘WidgetLookManager::getWidgetLookIterator’ method. From that query we can add a button array (see previous paragraph) with all the available widgets. Widget types which are not available yet can be marked in red or something.
Saving a looknfeel
The C++ call ‘writeXmlToStream’ can be used to serialize the current setup to a .looknfeel file.
Editing a widget
My first idea was to show a tree representing the looknfeel, and be able to edit settings via a grid. However a setting is very often more then just a Name, Value pair. For example a TextComponent defines an Area which is a componentAreaType. This block defines a choice of dimension types. Next, those choices themselves might contain sequences. This does not fit within a grid, and in order to avoid a very complex nested system of editing panels, a different apprauch was come up with…
Rich XML
Another option is to use a kind of “rich” XML edit panel, which is a read-only panel, but accepts user input through double mouse clicks and line browsing through the arrow keys. These mouse clicks will invoke helper dialogs, which will assure a safe -read: less error prone- way of editing the XML. However the user can still see what’s going on ‘under water’. Maybe there should be a ‘hardcore’ option which allows users to manually edit the XML for whatever reason. This panel will show the looknfeel XML contents of the currently edited widget (not the entire stream).
This panel has the following properties:
*Read-only (not mandatory).
*Coloured values (text between “’s), which clearly show that they can be modified.
*Ability to double-click these coloured texts in order to edit them. Editing is context sensitive; when the value is a ‘free’ value, an input dialog box is opened. When the text belongs to a choice-field (‘true/false’ or ‘add/subtract/multiply/divide’ or such) double-clicking will result in an option list dialog box. Choices are explained in detail in the “Choices” paragraph.
*A smart way to decide in which definition or block the cursor is currently residing. We can use the information from the XSD file in order to do this.
*The ability to add elements or blocks, based on the current position in the definition.
XML Editing Example
(note that i had coloured all values but this seems not to work on the forum...) This chapter will hopefully make clear –through a sample- how I see the XML editing panel work. For this example we start with a new looknfeel names “MyLook” and we start by editing the Button, which we initially call MyButton.
Now based on the Falagard.xsd file we determine what we may add, and come up with an initial XML content. Rule 1: When an element is optional (‘minocurs=0’), it will be added as a ‘Double-click here to add this’ line.
Code: Select all
<WidgetLook name="MyLook/MyButton">
[Double-click here to add a property definition]
[Double-click here to add a property link definition]
[Double-click here to add a property type]
[Double-click here to add a named area]
[Double-click here to add a child widget]
[Double-click here to add an imagery section]
[Double-click here to add a state imagery section]
</WidgetLook>
Now we add a property definition by clicking on the first line. The XML changes to:
Code: Select all
<WidgetLook name="MyLook/MyButton">
<PropertyDefinition name="Nameless" initialValue="" redrawOnWrite="false" redrawOnWrite="false" />
[Double-click here to add another property definition]
[Double-click here to add a property link definition]
[Double-click here to add a property type]
[Double-click here to add a named area]
[Double-click here to add a child widget]
[Double-click here to add an imagery section]
[Double-click here to add a state imagery section]
</WidgetLook>
Rule 2: we will use the default values for element attributes as found in the .XSD file. Rule 3: when an added line is part of a sequence, we add the option for another line of the same type.
Let’s now add an Imagery Section for the ‘hover’ state of the button. This is a bit more advanced:
Step 1: double-clicking the Imagery Section expands with the optional sub-tags.
Code: Select all
<WidgetLook name="MyLook/MyButton">
<PropertyDefinition name="Nameless" initialValue="" redrawOnWrite="true" redrawOnWrite="true" />
[Double-click here to add another property definition]
[Double-click here to add a property link definition]
[Double-click here to add a property type]
[Double-click here to add a named area]
[Double-click here to add a child widget]
<ImagerySection name="hover">
[Double-click here to select a colour type]
[Double-click here to add a FrameComponent definition]
[Double-click here to add a ImageryComponent definition]
[Double-click here to add a TextComponent definition]
</ImagerySection>
[Double-click here to add a state imagery section]
</WidgetLook>
Step 2: double-clicking the ImageryComponent Section expands too. Note that required options (such as the Area) are automatically added and don’t need be double-clicked in order to create, since that would invoke a needless extra step.
Code: Select all
<WidgetLook name="MyLook/MyButton">
<PropertyDefinition name="Nameless" initialValue="" redrawOnWrite="true" redrawOnWrite="true" />
[Double-click here to add another property definition]
[Double-click here to add a property link definition]
[Double-click here to add a property type]
[Double-click here to add a named area]
[Double-click here to add a child widget]
<ImagerySection name="label">
[Double-click here to choose a colour type]
[Double-click here to add a FrameComponent definition]
<ImageryComponent>
<Area>
[Double-click here to choose a dimension type]
</Area>
[Double-click here to choose an image type]
[Double-click here to choose a colour type]
[Double-click here to choose a vertical format type]
[Double-click here to choose a horizontal format type]
</ImageryComponent>
<!-- Rule 3: Again note the ‘another’ line
[Double-click here to add another ImageryComponent definition]
[Double-click here to add a TextComponent definition]
</ImagerySection>
[Double-click here to add a state imagery section]
</WidgetLook>
We double-click the dimension choice (see paragraph on choices), and choose the ‘dimensionType’ instead of the ‘settingByPropertyType’ option (both found in the XSD). This will automatically expand 4 elements with their default values. Since the dimension type’s attribute (dimensionTypeEnum) is also a choice value by it selves, we default to the first option which is ‘LeftEdge’. I consider this defaulting as ‘Rule 4’.
Code: Select all
<WidgetLook name="MyLook/MyButton">
<PropertyDefinition name="Nameless" initialValue="" redrawOnWrite="true" redrawOnWrite="true" />
[Double-click here to add another property definition]
[Double-click here to add a property link definition]
[Double-click here to add a property type]
[Double-click here to add a named area]
[Double-click here to add a child widget]
<ImagerySection name="label">
[Double-click here to select a colour type]
[Double-click here to add a FrameComponent definition]
<ImageryComponent>
<Area>
<!-- Rule 4: default to the first option
<Dim type="LeftEdge">
<!-- Rule 1: default arguments
<UnifiedDim scale="1" offset="0" />
</Dim>
<!—- Here we show changed values
<Dim type="TopEdge">
<ImageDim imageset="WindowsLook" image="ButtonHoverTop" dimension="Height" />
</Dim>
<Dim type="RightEdge">
<UnifiedDim scale="1" type="RightEdge">
<!—- Another nested option
<DimOperator op="Subtract">
<ImageDim imageset="WindowsLook" image="ButtonHoverRight" dimension="Width" />
</DimOperator>
[Double-click here to add another DimOperator]
</UnifiedDim>
</Dim>
<Dim type="BottomEdge">
<UnifiedDim scale="1" type="BottomEdge">
[Double-click here to add a DimOperator]
</UnifiedDim>
</Dim>
</Area>
[Double-click here to choose an image type]
[Double-click here to choose a colour type]
[Double-click here to choose a vertical format type]
[Double-click here to choose a horizontal format type]
</ImageryComponent>
[Double-click here to add a TextComponent definition]
</ImagerySection>
[Double-click here to add a state imagery section]
</WidgetLook>
And so on…
We can–for extra robustness- validate the current XML contents against the XSD file after a modification and present the user with warnings and errors. However -because of the readonly-ness of the panel- this should only occur in case of bugs in the tool’s editing capabilities. So it’s a good for testing purposes and bug hunting.
Choice and restrictions
Each “Double click to choose …” line corresponds to an input dialog which requests the user to make a choice. The following choice types have the following options, which will just be shown as strings to the user:
*ComponentAreaType: { Dim(dimensionType), AreaProperty(settingByPropertyType)}
*Dimension type: { UnifiedDim (unifiedDimType), AbsoluteDim (absoluteDimType), ImageDim(imageDimType), WidgetDim(widgetDimType), FontDim(fontDimType), propertyDim(propertyDimType)}
*Colour type: { Colour(colourType), Colours(colourRectType), ColourProperty(settingByPropertyType), ColourRectProperty(settingByPropertyType)}
*Image type : { Image(imageType), ImageProperty(settingByPropertyType)}
*Horizontal Format type: { HorzFormat (horzFormatType), HorzFormatProperty (settingByPropertyType)}
*Vertical Format type: { VertFormat(vertFormatType), VertFormatProperty(settingByPropertyType)}[/list]
After selecting an option, the editor will replace the XML line to an actual tag or block. For example when choosing ‘HorzFormat (horzFormatType)’ from the Horizontal Format type choice, this XML line will be inserted:
Code: Select all
<HorzFormat type="LeftAligned" />
Again note the default value (LeftAligned) for the ‘type’ attribute’s choice(horzFormatEnum).
Besides choices, there are also limitations such as enumeration values. This list is not pasted in here, because it’s quite big and very readable in XSD format. Just open the Falard.xsd file and look at this line and below:
Code: Select all
<xsd:simpleType name="propertyDimensionTypeEnum">
Discussion
Besides (of course) everything written above, there are some remaining points of discussion:
*When attributes are optional, such as ‘RedrawOnWrite’ in a PropertyDefinition, should we write them or not? The example shows then sometimes, just based on an existing (WindowsLook) file. Optional attributes could be marked in a different colour.
* …
Required CEGUI code changes
Probably independent of the actual implementation we choose, some changes are required to CEGUI itself in order to help querying the Falagard system a little better:
*Some Falagard classes should be enhanced with iterators on their privately defined elements. For example a ‘getImagerySectionIterator()’ in the ‘WidgetLookFeel’ class. And some more of those.
* …
Conclusion
You have just read my initial thought written down during a short holiday. Please comment on it, ask questions and so on. Thanks for your time!