Tue, 28 Sep 2021 07:31:52 +0300
* Design for TabPanel
--- a/Source/Gorgon/Geometry/Line.h Thu Sep 23 12:15:33 2021 +0300 +++ b/Source/Gorgon/Geometry/Line.h Tue Sep 28 07:31:52 2021 +0300 @@ -141,7 +141,13 @@ P_ End; }; - + template<class P_> + std::ostream &operator << (std::ostream &out, const Line<P_> &line) { + out << "[" << line.Start << " - " << line.End << "]"; + + return out; + } + //non-member operations: translate, scale, rotate, etc... } }
--- a/Source/Gorgon/UI/Template.cpp Thu Sep 23 12:15:33 2021 +0300 +++ b/Source/Gorgon/UI/Template.cpp Tue Sep 28 07:31:52 2021 +0300 @@ -1,354 +1,354 @@ - -#include "Template.h" -#include "../Graphics/Font.h" - -namespace Gorgon { namespace UI { - - /** - * @page ui User interface - * - * The user interface in Gorgon Library is based on the components system and is highly - * customizable. ComponentStack manages these components within a widget. There are - * different component types: containers (see @ref ContainerTemplate), placeholders (see - * @ref PlaceholderTemplate), graphics (see @ref GraphicsTemplate), and textholder (see - * @ref TextholderTemplate). All subcomponents should be created from the main Template. - * - * Each component has an index and containers have the index of the components that resides - * in them. There can be multiple components with the same index. ComponentCondition system - * is used to decide which components will exist at a given time. - * - * It is possible to control the transition of the widget from state to state by giving - * a specific component for the transition. This is generally used to animate state changes. - * It is also possible to use transition value channel to have value based animations. These - * animations can be reversed, even at the middle. In fact, even for regular bitmap - * animations, this is recommended as it can time the animation perfectly, allow it to be - * reversed at the middle of the transition (imagine mouse over). - * - * Components can be modified by ValueModification system. There are 4 widget controlled - * value channels and a fifth controlled by the transition. Values are expected to be in - * range from 0 to 1. But this can be changed by modifying value ranges. The aspect of the - * component that the value affects can also be controlled. Channels can be reordered to - * fit into the necessary order. Value changes can also be asked to be applied smoothly. But - * this is controlled through the widget. - * - * Component data can be obtained by DataEffect. Data can currently be a text or an image. - * There are various data effects that can be used by graphics and textholder templates. - * There are also conditions that trigger if a data channel has any data in it. - * - * Components can be repeated. The repeats are controlled by the widget. Containers are - * supported to be repeating structures, however, if it has subcomponents they should also - * set to repeat with the same repeat mode. This will not duplicate subcomponents into the - * container but rather uses the parent component's information in the subcomponents. Each - * repeat can have a different set value and condition. There are different repeating - * systems (such as minor and major) and each of these can be adjusted separately. - * - * Components can have tags to be accessed from the widgets. Some widgets needs specific - * tags to exist in the template to work. - * - * Components are placed according to a series of complicated but intuitive rules. For - * details of this system see @ref boxmodel. - * - * - * @subpage components - * - * @subpage boxmodel - * - * @subpage uiconditions - * - * @subpage valuechannels - * - * @subpage componentexample - * - * @subpage validators - */ - - - /** - * @page components Components - * - * Components are the building blocks of widgets. They are the visual elements - * that makes up most of the visual aspect of a widget. Components have templates, - * which they use as basis for their placement and drawing. ComponentTemplate is - * the base class for all component templates. Components have component index - * and component condition (see ComponentCondition) to control which components - * will be visible at a given instance. Components have the same component indices - * but only one will be shown at a time. The components that have more specific - * condition will have precedence. For instance, if two components have index of 2 - * and one has condition of ComponentCondition::Always and the other has condition - * of ComponentCondition::Focused, first component will be displayed unless the - * widget has received focus, in that case the second one will be shown. - * - * - * Every widget requires a container component at index 0 as the root. If this - * component has a non-0 size, the widget size will be fixed [this might change - * in the future]. - * - * - * Components can be modified by the widget data. This is controlled through data - * effect. Various data effects exist to suit different type of widgets. See the - * widget documentation for specific data exported by them. - * - * See @ref boxmodel to learn how the components are spaced out. - * - * See @ref componentexample to learn how to create a simple button - * - */ - - /** - * @page componentexample Component Example - * - * This example shows how to create a simple button. Create the following templates: - * - * @code - * ContainerTemplate, index = 0, background = bg image, set size, children = 1, 2 - * TextholderTemplate, index = 2, set font, data effect = Text - * @endcode - * - * These two templates will create a very simple button that has the background and - * it will display the text that is set by the programmer. If you wish to change the - * color of the text when the user moves mouse into the button, add the following - * template as well: - * - * @code - * TextholderTemplate index = 2, set font, data effect = Text, condition = Hover - * @endcode - * - * This component will override the previous if the mouse is over the button. In order - * to change the background when the user clicks the button, add the following: - * - * @code - * ContainerTemplate, index = 0, background = pressed, set size, condition = Down, - * children = 1, 2 - * @endcode - * - * This will change the background when the user presses the mouse button. Both hover and - * down conditions can occur at the same time. If user uses mouse to press the button - * they will both be active at the same time, however, if the user uses space bar to - * press the button when it is focused, only Down condition will be satisfied. In addition - * to these, it is helpful to provide a visual clue when the widget is focused. This - * is generally done by adding a focus rectangle like the following: - * - * @code - * GraphicsTemplate, index = 1, Drawable = focus rectangle, Positioning = Absolute, - * Size = 100, 100, Unit::Percent, set margin, condition = Focused - * @endcode - * - * This last piece will only be shown when the button is focused. There will be no errors - * when it is invisible even when the container lists this template as its child. - * - */ - - - /** - * @page boxmodel Component placement - * - * Apart from the top level container, each component should be in container. Components - * arrangement in a container first depends on component positioning. If a component is - * relatively positioned, then it will follow the container orientation to be stacked with - * the other relatively positioned components. - * - * Sizing - * ====== - * Components have multiple sizing modes. The first set of options control how the component - * will be sized in case it contains an image, text, or another component stack. If the size - * is Fixed, the size of the contents is ignored. Automatic uses the size of the contents. - * GrowOnly, ShrinkOnly options allow component to grow/shrink if the contents is larger/ - * smaller than the given size. Sizing can be adjusted separately for X and Y axis. When - * grow only and shrink only options are combined with relative sizing the size calculation - * is performed between fixed and always relative size calculation time. Mixing multiple - * automatic grow/shrink only, relatively sized components in the same container is not - * recommended. The system will try to fit automatic grow/shrink relatively sized components - * as best as possible with a single pass. Ex: There are two components, both are 50% in - * width and one of them is shrink only, container has 100px usable size. If the shrink only - * component has a content of 40px, the other component will be grown to fill the remaining - * 60px, even though it will be more than 50%. If this is not desired, shrink only component - * can be placed inside a 50% sized container, and its relative size should be set 100%. - * - * Component size can also be relative. In this case, container orientation plays an - * important role. If the relatively sized axis is not in the oriented direction, then the - * entire usable size of the parent is used for the calculation. However, if the axis is the - * oriented direction, then only the unused space is considered during the relative size - * calculation. For instance, in a horizontally oriented container, assume there are two - * components. One has a fixed width of 50px and the other is set to 100% and the container - * has 200px usable width. Assuming no additional spacing, second component will have 150px - * width. This calculation depends on how the components are anchored and margin/padding/ - * indent/border size. - * - * Size of the component can be influenced by the value channels. In this case, the size of - * component is used as additional size on top of the relative size depending on the channel - * value. - * - * It is possible for widgets to specify additional size to tags. In these cases, the given - * size will be used as an addition to the original size. It is not recommended to use - * automatic sizing with tags that the widgets can use (e.g., ContentsTag). - * - * - * Spacing - * ======= - * In this system 4 spacing methods are used. First method is border size. Border size is - * excluded from the internal area of a container. Thus (0, 0), does not include the border. - * In this system, borders are not drawn automatically, they are a part of the background - * image, therefore, border size is not calculated. It should be supplied. - * - * The second spacing is provided by the padding of the container. Main difference of - * padding is that it collapses with the margin of the components as well as being counted - * in the container. If the padding for any edge is negative, it will not be collapsed. - * - * Margins are spacing around the components. Unless negative, they collapse. This means if - * two components are anchored together, the margin between them is the maximum of the - * margin their touching edges. Negative margins are always added. - * - * Indent is the last spacing. It is additive and only used if a component is anchored to - * its parent. This is very useful in repeated or conditional components. For instance, if - * a button can have icon with text. You may want extra spacing on the text if there is no - * icon. This can be facilitated using Icon1IsSet condition and indent. - * - * All spacing except for border size can be relative. For padding, the size of the - * component excluding the borders is used. For margin and indent, size of the parent - * excluding the borders is used. 50% margin left will effectively start a component from - * the center. However, anchoring should be used for this case. - * - * - * Anchoring - * ========= - * All components are placed according to their anchor points. Relatively positioned - * components can be anchored to another component, but absolutely positioned components - * are always anchored to their parent. Depending on the orientation, relative anchoring - * will be done to left/right or top/bottom. It is possible for anchoring to cause - * artifacts, thus it should be used properly. - * - * Absolute anchoring is simple. It uses parent and anchor point. In this anchoring, the - * component should be inside the parent. This means, if horizontal part of the anchor point - * is left, then parent anchor should either be left or center, so that the component will - * be placed in the parent. Otherwise the component will be placed outside the parent. If - * an absolutely positioned object is attached to left/top or right/bottom of its parent's - * center/middle, then the effective size of the container if only include right/bottom or - * left/top portion of the container. If spacing for both sides are equal, this will cut the - * usable size by half. This feature makes it easier to deal with center starting - * components. - * - * Relative anchoring requires previous component anchor dictated by the current component - * and parent anchor. These target anchor points should match. Following previous example, - * where anchor point was left, parent anchor should again either be left or center. - * Assuming orientation is horizontal, previous anchor should be right, anchoring the - * component to the end of the previous one. In a container, there can be two separate - * anchor stacks, one starting from left to right and the other starting from right to - * left. This also works with vertical layouts (top to bottom and bottom to top). This is - * controlled by the combination of the anchor and previous component anchor. If anchor is - * specified as right and previous anchor is left (or none and parent anchor is right), - * then the component will be anchored from the right. If previous anchor is none, component - * is always anchored to the parent. This may cause components to overlap. Components can - * only be anchored to their parents or other relative components. - * - * First baseline anchor uses the baseline of the first text line. In the calculations, it - * is considered top aligned. Last baseline uses the baseline of the last text line. It is - * considered as bottom alignment. If there is no text, first baseline is calculated from - * the top, last baseline is calculated from the bottom. Baseline can be specified in - * template. When it is specified, that value will be used instead of calculated baseline. - * Last baseline alignments should only be used for textholders. - * - * Positioning - * =========== - * The position of the component effects its place from the anchor. If anchored by left/top - * or center/middle x/y position is added to the anchor point. If anchored by right/bottom, - * x/y position is subtracted. - * - * In AbsoluteSliding positioning the component anchors to its parent. For relative size, - * entire usable area of the parent is used. If the position is specified as percentage, - * remaining area after the component is placed is used. Ex: parent width: 100, component - * width: 50, parent padding: 10, component x position: 50%, margin: 0. In this case, - * component width and padding is subtracted from parent width, 30px remains. This size is - * used to calculate percentage position, which is 15px. If an component's size is 100%, - * percentage base positioning will not have any effect on the position of the component. - * - * Relative positioning is very similar to absolute positioning except the component can be - * anchored to other components and its percent based metrics uses remaining size of the - * parent in the oriented direction. - * - * Absolute positioning is classical absolute positioning in many systems. It uses entirety - * of the parent size to calculate percentage based positioning. In other aspects, it is - * same with absolute sliding positioning. - * - * PolarAbsolute changes coordinate system to polar coordinates. X axis becomes radius and - * Y axis becomes angle in degrees. Center is used to calculate transformation point. While - * calculating center point Absolute positioning is used. If radius is percentage based, the - * maximum size that will ensure component will stay within the parent will be used. Parent - * anchor is used to decide how to calculate this length. For instance, if the anchor point - * is TopCenter, then the max radius calculation assumes angle will change from 180-360. - * This currently does not take value range into consideration. If desired, em size could - * also be used for angle, which is set to 45deg. Component is then placed according to its - * center point. To use PolarAbsolute correctly, you should set center of parent, center of - * component as well as parent anchor of the component (not self anchor) correctly. Setting - * parent anchor also adjusts maximum and starting angle so that the both radius 0-100% and - * angle 0-100% will stay within the container. Angle always works counter clockwise. Angle - * EM size is not changed by the anchor point, however, it is also affected by the starting - * point. Also, when percentages or value modification is involved, it is advised to set - * center point of the component so that it will be within the object. This offset will not - * be automatically done and will leave portions of the object outside its parent. - * - * Position can be affected by the value modification system. In this case specified - * position is used as an addition with a percentage based value. - * - * It is possible for widgets to specify additional position for a specific tag. It is - * recommended to anchor such components to top left and instead of using position to place - * them, use spacing. Only one tag currently is used for position modification: ContentsTag. - * - * - */ - - - /** - * @page uiconditions Conditions - * - * There are many conditions that will effect the visibility/selection of each components. - * Using conditions, visuals of widgets can be modified. Conditions are supplied by the - * widgets depending on their states. A simple example will be mouse over. - * - * Only one component can be present for every index. When there are multiple components - * for the same index is present, the component to be shown is determined by the active - * conditions. If there are no conditions present, only components with Always condition - * will be visible. If there is a condition that matches a component, that particular - * component will be visible for that index. If there are multiple conditions active for - * the same index, the last condition applied will take precedence. Not all conditions are - * supported by all widgets. For the list of conditions see @ref ComponentCondition. - * - * Combining multiple conditions can be done by using containers. Containers could be set - * to obtain the size of its contents, with no spacing, it will not effect anything - * visually. Having separate conditions for the container and the component will act as and - * operation as both conditions should be present for the component to be visible. For or - * operation, you can have two containers containing the same index with different - * conditions. Indexes are global, therefore, if one of the containers is visible on the - * screen, component will be visible. A component cannot be in two separate containers at - * the *same* time. Thus the containers should have the same index for exclusivity. - * - * Conditions can transition from one to another. These are also controlled by widgets. For - * transitions to work, it should be adjusted from the Template. First the transition - * duration should be set. Then if the transition can be reversed, which is recommended, it - * should be set as well. Finally, there should be a component with transition from first - * condition to the second. This can be done by supplying two conditions to Add component - * functions. With reversed transition and using transition value channel to control the - * effect, one component can handle both conditions and the transition to both directions. - * - * For instance, for a checkbox, an animation from unchecked to checked can be used for - * unchecked (Always), checked (State2), Always > State2, State2 > Always by waiting at - * first frame, waiting at last frame, running the animation forwards and running the - * animation backwards. All of which can easily be handled by setting value source to - * Transition, value modification to ModifyAnimation, condition should be set to Always > - * State2 and finally, the durations for Always > State2 and State2 > Always transitions - * should be set. - * - */ - - /** - * @page valuechannels Value channels and modification - * - * Value modification is an integral part of the UI mechanics. It allows widgets to specify - * values up to 4 channels. The components can be setup to be modified by these channels by - * various effects. In addition to these standard 4 channels, there is also transition - * channel, the value of which changes with the transition of the conditions. - * - */ - + +#include "Template.h" +#include "../Graphics/Font.h" + +namespace Gorgon { namespace UI { + + /** + * @page ui User interface + * + * The user interface in Gorgon Library is based on the components system and is highly + * customizable. ComponentStack manages these components within a widget. There are + * different component types: containers (see @ref ContainerTemplate), placeholders (see + * @ref PlaceholderTemplate), graphics (see @ref GraphicsTemplate), and textholder (see + * @ref TextholderTemplate). All subcomponents should be created from the main Template. + * + * Each component has an index and containers have the index of the components that resides + * in them. There can be multiple components with the same index. ComponentCondition system + * is used to decide which components will exist at a given time. + * + * It is possible to control the transition of the widget from state to state by giving + * a specific component for the transition. This is generally used to animate state changes. + * It is also possible to use transition value channel to have value based animations. These + * animations can be reversed, even at the middle. In fact, even for regular bitmap + * animations, this is recommended as it can time the animation perfectly, allow it to be + * reversed at the middle of the transition (imagine mouse over). + * + * Components can be modified by ValueModification system. There are 4 widget controlled + * value channels and a fifth controlled by the transition. Values are expected to be in + * range from 0 to 1. But this can be changed by modifying value ranges. The aspect of the + * component that the value affects can also be controlled. Channels can be reordered to + * fit into the necessary order. Value changes can also be asked to be applied smoothly. But + * this is controlled through the widget. + * + * Component data can be obtained by DataEffect. Data can currently be a text or an image. + * There are various data effects that can be used by graphics and textholder templates. + * There are also conditions that trigger if a data channel has any data in it. + * + * Components can be repeated. The repeats are controlled by the widget. Containers are + * supported to be repeating structures, however, if it has subcomponents they should also + * set to repeat with the same repeat mode. This will not duplicate subcomponents into the + * container but rather uses the parent component's information in the subcomponents. Each + * repeat can have a different set value and condition. There are different repeating + * systems (such as minor and major) and each of these can be adjusted separately. + * + * Components can have tags to be accessed from the widgets. Some widgets needs specific + * tags to exist in the template to work. + * + * Components are placed according to a series of complicated but intuitive rules. For + * details of this system see @ref boxmodel. + * + * + * @subpage components + * + * @subpage boxmodel + * + * @subpage uiconditions + * + * @subpage valuechannels + * + * @subpage componentexample + * + * @subpage validators + */ + + + /** + * @page components Components + * + * Components are the building blocks of widgets. They are the visual elements + * that makes up most of the visual aspect of a widget. Components have templates, + * which they use as basis for their placement and drawing. ComponentTemplate is + * the base class for all component templates. Components have component index + * and component condition (see ComponentCondition) to control which components + * will be visible at a given instance. Components have the same component indices + * but only one will be shown at a time. The components that have more specific + * condition will have precedence. For instance, if two components have index of 2 + * and one has condition of ComponentCondition::Always and the other has condition + * of ComponentCondition::Focused, first component will be displayed unless the + * widget has received focus, in that case the second one will be shown. + * + * + * Every widget requires a container component at index 0 as the root. If this + * component has a non-0 size, the widget size will be fixed [this might change + * in the future]. + * + * + * Components can be modified by the widget data. This is controlled through data + * effect. Various data effects exist to suit different type of widgets. See the + * widget documentation for specific data exported by them. + * + * See @ref boxmodel to learn how the components are spaced out. + * + * See @ref componentexample to learn how to create a simple button + * + */ + + /** + * @page componentexample Component Example + * + * This example shows how to create a simple button. Create the following templates: + * + * @code + * ContainerTemplate, index = 0, background = bg image, set size, children = 1, 2 + * TextholderTemplate, index = 2, set font, data effect = Text + * @endcode + * + * These two templates will create a very simple button that has the background and + * it will display the text that is set by the programmer. If you wish to change the + * color of the text when the user moves mouse into the button, add the following + * template as well: + * + * @code + * TextholderTemplate index = 2, set font, data effect = Text, condition = Hover + * @endcode + * + * This component will override the previous if the mouse is over the button. In order + * to change the background when the user clicks the button, add the following: + * + * @code + * ContainerTemplate, index = 0, background = pressed, set size, condition = Down, + * children = 1, 2 + * @endcode + * + * This will change the background when the user presses the mouse button. Both hover and + * down conditions can occur at the same time. If user uses mouse to press the button + * they will both be active at the same time, however, if the user uses space bar to + * press the button when it is focused, only Down condition will be satisfied. In addition + * to these, it is helpful to provide a visual clue when the widget is focused. This + * is generally done by adding a focus rectangle like the following: + * + * @code + * GraphicsTemplate, index = 1, Drawable = focus rectangle, Positioning = Absolute, + * Size = 100, 100, Unit::Percent, set margin, condition = Focused + * @endcode + * + * This last piece will only be shown when the button is focused. There will be no errors + * when it is invisible even when the container lists this template as its child. + * + */ + + + /** + * @page boxmodel Component placement + * + * Apart from the top level container, each component should be in container. Components + * arrangement in a container first depends on component positioning. If a component is + * relatively positioned, then it will follow the container orientation to be stacked with + * the other relatively positioned components. + * + * Sizing + * ====== + * Components have multiple sizing modes. The first set of options control how the component + * will be sized in case it contains an image, text, or another component stack. If the size + * is Fixed, the size of the contents is ignored. Automatic uses the size of the contents. + * GrowOnly, ShrinkOnly options allow component to grow/shrink if the contents is larger/ + * smaller than the given size. Sizing can be adjusted separately for X and Y axis. When + * grow only and shrink only options are combined with relative sizing the size calculation + * is performed between fixed and always relative size calculation time. Mixing multiple + * automatic grow/shrink only, relatively sized components in the same container is not + * recommended. The system will try to fit automatic grow/shrink relatively sized components + * as best as possible with a single pass. Ex: There are two components, both are 50% in + * width and one of them is shrink only, container has 100px usable size. If the shrink only + * component has a content of 40px, the other component will be grown to fill the remaining + * 60px, even though it will be more than 50%. If this is not desired, shrink only component + * can be placed inside a 50% sized container, and its relative size should be set 100%. + * + * Component size can also be relative. In this case, container orientation plays an + * important role. If the relatively sized axis is not in the oriented direction, then the + * entire usable size of the parent is used for the calculation. However, if the axis is the + * oriented direction, then only the unused space is considered during the relative size + * calculation. For instance, in a horizontally oriented container, assume there are two + * components. One has a fixed width of 50px and the other is set to 100% and the container + * has 200px usable width. Assuming no additional spacing, second component will have 150px + * width. This calculation depends on how the components are anchored and margin/padding/ + * indent/border size. + * + * Size of the component can be influenced by the value channels. In this case, the size of + * component is used as additional size on top of the relative size depending on the channel + * value. + * + * It is possible for widgets to specify additional size to tags. In these cases, the given + * size will be used as an addition to the original size. It is not recommended to use + * automatic sizing with tags that the widgets can use (e.g., ContentsTag). + * + * + * Spacing + * ======= + * In this system 4 spacing methods are used. First method is border size. Border size is + * excluded from the internal area of a container. Thus (0, 0), does not include the border. + * In this system, borders are not drawn automatically, they are a part of the background + * image, therefore, border size is not calculated. It should be supplied. + * + * The second spacing is provided by the padding of the container. Main difference of + * padding is that it collapses with the margin of the components as well as being counted + * in the container. If the padding for any edge is negative, it will not be collapsed. + * + * Margins are spacing around the components. Unless negative, they collapse. This means if + * two components are anchored together, the margin between them is the maximum of the + * margin their touching edges. Negative margins are always added. + * + * Indent is the last spacing. It is additive and only used if a component is anchored to + * its parent. This is very useful in repeated or conditional components. For instance, if + * a button can have icon with text. You may want extra spacing on the text if there is no + * icon. This can be facilitated using Icon1IsSet condition and indent. + * + * All spacing except for border size can be relative. For padding, the size of the + * component excluding the borders is used. For margin and indent, size of the parent + * excluding the borders is used. 50% margin left will effectively start a component from + * the center. However, anchoring should be used for this case. + * + * + * Anchoring + * ========= + * All components are placed according to their anchor points. Relatively positioned + * components can be anchored to another component, but absolutely positioned components + * are always anchored to their parent. Depending on the orientation, relative anchoring + * will be done to left/right or top/bottom. It is possible for anchoring to cause + * artifacts, thus it should be used properly. + * + * Absolute anchoring is simple. It uses parent and anchor point. In this anchoring, the + * component should be inside the parent. This means, if horizontal part of the anchor point + * is left, then parent anchor should either be left or center, so that the component will + * be placed in the parent. Otherwise the component will be placed outside the parent. If + * an absolutely positioned object is attached to left/top or right/bottom of its parent's + * center/middle, then the effective size of the container if only include right/bottom or + * left/top portion of the container. If spacing for both sides are equal, this will cut the + * usable size by half. This feature makes it easier to deal with center starting + * components. + * + * Relative anchoring requires previous component anchor dictated by the current component + * and parent anchor. These target anchor points should match. Following previous example, + * where anchor point was left, parent anchor should again either be left or center. + * Assuming orientation is horizontal, previous anchor should be right, anchoring the + * component to the end of the previous one. In a container, there can be two separate + * anchor stacks, one starting from left to right and the other starting from right to + * left. This also works with vertical layouts (top to bottom and bottom to top). This is + * controlled by the combination of the anchor and previous component anchor. If anchor is + * specified as right and previous anchor is left (or none and parent anchor is right), + * then the component will be anchored from the right. If previous anchor is none, component + * is always anchored to the parent. This may cause components to overlap. Components can + * only be anchored to their parents or other relative components. + * + * First baseline anchor uses the baseline of the first text line. In the calculations, it + * is considered top aligned. Last baseline uses the baseline of the last text line. It is + * considered as bottom alignment. If there is no text, first baseline is calculated from + * the top, last baseline is calculated from the bottom. Baseline can be specified in + * template. When it is specified, that value will be used instead of calculated baseline. + * Last baseline alignments should only be used for textholders. + * + * Positioning + * =========== + * The position of the component effects its place from the anchor. If anchored by left/top + * or center/middle x/y position is added to the anchor point. If anchored by right/bottom, + * x/y position is subtracted. + * + * In AbsoluteSliding positioning the component anchors to its parent. For relative size, + * entire usable area of the parent is used. If the position is specified as percentage, + * remaining area after the component is placed is used. Ex: parent width: 100, component + * width: 50, parent padding: 10, component x position: 50%, margin: 0. In this case, + * component width and padding is subtracted from parent width, 30px remains. This size is + * used to calculate percentage position, which is 15px. If an component's size is 100%, + * percentage base positioning will not have any effect on the position of the component. + * + * Relative positioning is very similar to absolute positioning except the component can be + * anchored to other components and its percent based metrics uses remaining size of the + * parent in the oriented direction. + * + * Absolute positioning is classical absolute positioning in many systems. It uses entirety + * of the parent size to calculate percentage based positioning. In other aspects, it is + * same with absolute sliding positioning. + * + * PolarAbsolute changes coordinate system to polar coordinates. X axis becomes radius and + * Y axis becomes angle in degrees. Center is used to calculate transformation point. While + * calculating center point Absolute positioning is used. If radius is percentage based, the + * maximum size that will ensure component will stay within the parent will be used. Parent + * anchor is used to decide how to calculate this length. For instance, if the anchor point + * is TopCenter, then the max radius calculation assumes angle will change from 180-360. + * This currently does not take value range into consideration. If desired, em size could + * also be used for angle, which is set to 45deg. Component is then placed according to its + * center point. To use PolarAbsolute correctly, you should set center of parent, center of + * component as well as parent anchor of the component (not self anchor) correctly. Setting + * parent anchor also adjusts maximum and starting angle so that the both radius 0-100% and + * angle 0-100% will stay within the container. Angle always works counter clockwise. Angle + * EM size is not changed by the anchor point, however, it is also affected by the starting + * point. Also, when percentages or value modification is involved, it is advised to set + * center point of the component so that it will be within the object. This offset will not + * be automatically done and will leave portions of the object outside its parent. + * + * Position can be affected by the value modification system. In this case specified + * position is used as an addition with a percentage based value. + * + * It is possible for widgets to specify additional position for a specific tag. It is + * recommended to anchor such components to top left and instead of using position to place + * them, use spacing. Only one tag currently is used for position modification: ContentsTag. + * + * + */ + + + /** + * @page uiconditions Conditions + * + * There are many conditions that will effect the visibility/selection of each components. + * Using conditions, visuals of widgets can be modified. Conditions are supplied by the + * widgets depending on their states. A simple example will be mouse over. + * + * Only one component can be present for every index. When there are multiple components + * for the same index is present, the component to be shown is determined by the active + * conditions. If there are no conditions present, only components with Always condition + * will be visible. If there is a condition that matches a component, that particular + * component will be visible for that index. If there are multiple conditions active for + * the same index, the last condition applied will take precedence. Not all conditions are + * supported by all widgets. For the list of conditions see @ref ComponentCondition. + * + * Combining multiple conditions can be done by using containers. Containers could be set + * to obtain the size of its contents, with no spacing, it will not effect anything + * visually. Having separate conditions for the container and the component will act as and + * operation as both conditions should be present for the component to be visible. For or + * operation, you can have two containers containing the same index with different + * conditions. Indexes are global, therefore, if one of the containers is visible on the + * screen, component will be visible. A component cannot be in two separate containers at + * the *same* time. Thus the containers should have the same index for exclusivity. + * + * Conditions can transition from one to another. These are also controlled by widgets. For + * transitions to work, it should be adjusted from the Template. First the transition + * duration should be set. Then if the transition can be reversed, which is recommended, it + * should be set as well. Finally, there should be a component with transition from first + * condition to the second. This can be done by supplying two conditions to Add component + * functions. With reversed transition and using transition value channel to control the + * effect, one component can handle both conditions and the transition to both directions. + * + * For instance, for a checkbox, an animation from unchecked to checked can be used for + * unchecked (Always), checked (State2), Always > State2, State2 > Always by waiting at + * first frame, waiting at last frame, running the animation forwards and running the + * animation backwards. All of which can easily be handled by setting value source to + * Transition, value modification to ModifyAnimation, condition should be set to Always > + * State2 and finally, the durations for Always > State2 and State2 > Always transitions + * should be set. + * + */ + + /** + * @page valuechannels Value channels and modification + * + * Value modification is an integral part of the UI mechanics. It allows widgets to specify + * values up to 4 channels. The components can be setup to be modified by these channels by + * various effects. In addition to these standard 4 channels, there is also transition + * channel, the value of which changes with the transition of the conditions. + * + */ + @@ -364,7 +364,8 @@ additional(std::move(other.additional)), spacing(std::move(other.spacing)), unitwidth(std::move(other.unitwidth)), - resizehandlesize(std::move(other.resizehandlesize)) + resizehandlesize(std::move(other.resizehandlesize)), + direction(std::move(other.direction)) { auto token = tokens.begin(); for(auto &c : components) { @@ -379,113 +380,113 @@ } } - PlaceholderTemplate& Template::AddPlaceholder(int index, ComponentCondition from, ComponentCondition to){ - auto obj = new PlaceholderTemplate(); - components.Add(obj); - - obj->SetIndex(index); - obj->SetCondition(from, to); - - tokens.push_back( - obj->ChangedEvent.Register(ChangedEvent, &Event<Template>::operator ()) - ); - - ChangedEvent(); - - return *obj; - } - - ContainerTemplate& Template::AddContainer(int index, ComponentCondition from, ComponentCondition to){ - auto obj = new ContainerTemplate(); - components.Add(obj); - - obj->SetIndex(index); - obj->SetCondition(from, to); - - tokens.push_back( - obj->ChangedEvent.Register(ChangedEvent, &Event<Template>::operator ()) - ); - - ChangedEvent(); - - return *obj; - } - - IgnoredTemplate& Template::AddIgnored(int index, ComponentCondition from, ComponentCondition to){ - auto obj = new IgnoredTemplate(); - components.Add(obj); - - obj->SetIndex(index); - obj->SetCondition(from, to); - - tokens.push_back( - obj->ChangedEvent.Register(ChangedEvent, &Event<Template>::operator ()) - ); - - ChangedEvent(); - - return *obj; - } - - TextholderTemplate& Template::AddTextholder(int index, ComponentCondition from, ComponentCondition to){ - auto obj = new TextholderTemplate(); - components.Add(obj); - - obj->SetIndex(index); - obj->SetCondition(from, to); - - tokens.push_back( - obj->ChangedEvent.Register(ChangedEvent, &Event<Template>::operator ()) - ); - - ChangedEvent(); - - return *obj; - } - - GraphicsTemplate& Template::AddGraphics(int index, ComponentCondition from, ComponentCondition to){ - auto obj = new GraphicsTemplate(); - components.Add(obj); - - obj->SetIndex(index); - obj->SetCondition(from, to); - - tokens.push_back( - obj->ChangedEvent.Register(ChangedEvent, &Event<Template>::operator ()) - ); - - ChangedEvent(); - - return *obj; - } - - ComponentTemplate& Template::Release(int index) { - auto &c = components[index]; - - c.ChangedEvent.Unregister(tokens[index]); - - tokens.erase(tokens.begin() + index); - - components.Remove(index); - - ChangedEvent(); - - return c; - } - - void Template::Assume(ComponentTemplate& component) { - components.Add(component); - - tokens.push_back( - component.ChangedEvent.Register(ChangedEvent, &Event<Template>::operator ()) - ); - - ChangedEvent(); - } - - /// Returns if this text holder can perform rendering - bool TextholderTemplate::IsReady() const { - return renderer != nullptr && renderer->IsReady(); - } - -} } + PlaceholderTemplate& Template::AddPlaceholder(int index, ComponentCondition from, ComponentCondition to){ + auto obj = new PlaceholderTemplate(); + components.Add(obj); + + obj->SetIndex(index); + obj->SetCondition(from, to); + + tokens.push_back( + obj->ChangedEvent.Register(ChangedEvent, &Event<Template>::operator ()) + ); + + ChangedEvent(); + + return *obj; + } + + ContainerTemplate& Template::AddContainer(int index, ComponentCondition from, ComponentCondition to){ + auto obj = new ContainerTemplate(); + components.Add(obj); + + obj->SetIndex(index); + obj->SetCondition(from, to); + + tokens.push_back( + obj->ChangedEvent.Register(ChangedEvent, &Event<Template>::operator ()) + ); + + ChangedEvent(); + + return *obj; + } + + IgnoredTemplate& Template::AddIgnored(int index, ComponentCondition from, ComponentCondition to){ + auto obj = new IgnoredTemplate(); + components.Add(obj); + + obj->SetIndex(index); + obj->SetCondition(from, to); + + tokens.push_back( + obj->ChangedEvent.Register(ChangedEvent, &Event<Template>::operator ()) + ); + + ChangedEvent(); + + return *obj; + } + + TextholderTemplate& Template::AddTextholder(int index, ComponentCondition from, ComponentCondition to){ + auto obj = new TextholderTemplate(); + components.Add(obj); + + obj->SetIndex(index); + obj->SetCondition(from, to); + + tokens.push_back( + obj->ChangedEvent.Register(ChangedEvent, &Event<Template>::operator ()) + ); + + ChangedEvent(); + + return *obj; + } + + GraphicsTemplate& Template::AddGraphics(int index, ComponentCondition from, ComponentCondition to){ + auto obj = new GraphicsTemplate(); + components.Add(obj); + + obj->SetIndex(index); + obj->SetCondition(from, to); + + tokens.push_back( + obj->ChangedEvent.Register(ChangedEvent, &Event<Template>::operator ()) + ); + + ChangedEvent(); + + return *obj; + } + + ComponentTemplate& Template::Release(int index) { + auto &c = components[index]; + + c.ChangedEvent.Unregister(tokens[index]); + + tokens.erase(tokens.begin() + index); + + components.Remove(index); + + ChangedEvent(); + + return c; + } + + void Template::Assume(ComponentTemplate& component) { + components.Add(component); + + tokens.push_back( + component.ChangedEvent.Register(ChangedEvent, &Event<Template>::operator ()) + ); + + ChangedEvent(); + } + + /// Returns if this text holder can perform rendering + bool TextholderTemplate::IsReady() const { + return renderer != nullptr && renderer->IsReady(); + } + +} }
--- a/Source/Gorgon/UI/Template.h Thu Sep 23 12:15:33 2021 +0300 +++ b/Source/Gorgon/UI/Template.h Tue Sep 28 07:31:52 2021 +0300 @@ -369,6 +369,13 @@ /// Do not use this condition, this is to size the arrays. Max, }; + + ENUMCLASS Direction { + LeftToRight, + TopToBottom, + RightToLeft, + BottomToTop + }; /// Returns if the given condition is related to mouse events inline bool IsMouseRelated(ComponentCondition condition) { @@ -636,7 +643,18 @@ return components.end(); } - + /// In some widgets there is a direction which controls where + /// the subcomponents are placed. Direction controls this. + void SetDirection(Direction value) { + direction = value; + ChangedEvent(); + } + + /// In some widgets there is a direction which controls where + /// the subcomponents are placed. Direction controls this. + Direction GetDirection() const { + return direction; + } /// This event is fired whenever template or its components are changed. @@ -656,6 +674,8 @@ int unitwidth = 25; int resizehandlesize = -1; + Direction direction; + //Do not forget to update move constructor! }; @@ -947,7 +967,7 @@ MaximizeTag, HelpTag, ButtonTag, - DialogButtonsTag, + ButtonsTag, }; /// Some components are repeated along some axis, this property controls how they will be
--- a/Source/Gorgon/Widgets/DialogWindow.cpp Thu Sep 23 12:15:33 2021 +0300 +++ b/Source/Gorgon/Widgets/DialogWindow.cpp Tue Sep 28 07:31:52 2021 +0300 @@ -40,7 +40,7 @@ Center(); } - int ind = stack.IndexOfTag(UI::ComponentTemplate::DialogButtonsTag); + int ind = stack.IndexOfTag(UI::ComponentTemplate::ButtonsTag); if(ind != -1) { buttonsarea.SetLayer(stack.GetLayerOf(ind));
--- a/Source/Gorgon/Widgets/Generator.cpp Thu Sep 23 12:15:33 2021 +0300 +++ b/Source/Gorgon/Widgets/Generator.cpp Tue Sep 28 07:31:52 2021 +0300 @@ -17,7 +17,7 @@ # include <fontconfig/fontconfig.h> #endif -#define MSVC_BUG(MACRO, ARGS) MACRO ARGS // name to remind that bug fix is due to MSVC :-) +#define MSVC_BUG(MACRO, ARGS) MACRO ARGS // name to remind that bug fix is due to MSVC #define CONCATE_(X,Y) X##Y #define CONCATE(X,Y) CONCATE_(X,Y) @@ -2560,7 +2560,7 @@ btnplace.SetMargin(0, spacing, 0, 0); btnplace.SetPositioning(UI::ComponentTemplate::Relative); btnplace.SetAnchor(UI::Anchor::BottomCenter, UI::Anchor::TopCenter, UI::Anchor::TopCenter); - btnplace.SetTag(UI::ComponentTemplate::DialogButtonsTag); + btnplace.SetTag(UI::ComponentTemplate::ButtonsTag); auto &btn = temp.AddPlaceholder(maxind+1, UI::ComponentCondition::Always); btn.SetTemplate(btndiag); @@ -2711,20 +2711,55 @@ return temp; } - - UI::Template SimpleGenerator::Textarea() { - Geometry::Size defsize = {GetUnitSize(6), GetUnitSize(3)}; - + + UI::Template SimpleGenerator::TabPanel() { + Geometry::Size defsize = { + GetUnitSize(6) + Border.Width * 2 + spacing * 2, + GetUnitSize(10) + Border.Width * 2 + spacing * 2 + }; + UI::Template temp = maketemplate(); temp.SetSpacing(spacing); temp.SetSize(defsize); - - + + //Main container + temp.AddContainer(0, UI::ComponentCondition::Always) + .AddIndex(1) //button container + .AddIndex(2) //panel container + .SetOrientation(Graphics::Orientation::Vertical) + ; + + //Button container + temp.AddContainer(1, UI::ComponentCondition::Always) + .AddIndex(3) //button panel + .AddIndex(4) //additional graphics + .SetSizing(UI::ComponentTemplate::Fixed, UI::ComponentTemplate::Automatic) + ; + + auto &buttonspanel = temp.AddPlaceholder(3, UI::ComponentCondition::Always); + buttonspanel.SetTag(UI::ComponentTemplate::ButtonsTag); + buttonspanel.SetSize({100, UI::Dimension::Percent}, unitsize); + buttonspanel.SetSizing(UI::ComponentTemplate::Automatic, UI::ComponentTemplate::Fixed); + + auto &graph = temp.AddGraphics(4, UI::ComponentCondition::Always); + + + return temp; + } + + UI::Template SimpleGenerator::Textarea() { + Geometry::Size defsize = {GetUnitSize(6), GetUnitSize(3)}; + + UI::Template temp = maketemplate(); + temp.SetSpacing(spacing); + temp.SetSize(defsize); + + auto &bg = temp.AddContainer(0, UI::ComponentCondition::Always) .AddIndex(1) //border .AddIndex(2) //boxed content ; - + auto setupborder = [&](auto &anim, UI::ComponentCondition condition) { auto &bg = temp.AddContainer(1, condition); bg.Background.SetAnimation(anim); @@ -2736,7 +2771,7 @@ setupborder(A(Edit, Hover), UI::ComponentCondition::Hover); setupborder(A(Edit, Down), UI::ComponentCondition::Readonly); setupborder(A(Edit, Disabled), UI::ComponentCondition::Disabled); - + auto &boxed = temp.AddContainer(2, UI::ComponentCondition::Always) .AddIndex(3) //clip .AddIndex(4) //focus @@ -2745,7 +2780,7 @@ boxed.SetBorderSize(Border.Width); boxed.SetPadding(std::max(Border.Radius / 2, Focus.Spacing)); boxed.SetPositioning(UI::ComponentTemplate::Absolute); - + auto &clip = temp.AddContainer(3, UI::ComponentCondition::Always) .AddIndex(5) ; @@ -2753,7 +2788,7 @@ clip.SetSize(100, 100, UI::Dimension::Percent); clip.SetTag(UI::ComponentTemplate::ViewPortTag); clip.SetAnchor(UI::Anchor::TopLeft, UI::Anchor::TopLeft, UI::Anchor::TopLeft); - + //Contents auto &content = temp.AddContainer(5, UI::ComponentCondition::Always) .AddIndex(6) //text @@ -2764,8 +2799,8 @@ content.SetPositioning(UI::ComponentTemplate::Absolute); content.SetAnchor(UI::Anchor::TopLeft, UI::Anchor::TopLeft, UI::Anchor::TopLeft); content.SetTag(UI::ComponentTemplate::ViewPortTag); - - + + //Text auto setuptext = [&](Graphics::RGBA color, UI::ComponentCondition condition) { auto &txt = temp.AddTextholder(6, condition); @@ -2776,12 +2811,12 @@ txt.SetPositioning(UI::ComponentTemplate::Absolute); txt.SetTag(UI::ComponentTemplate::ContentsTag); }; - + setuptext(FgC(Regular), UI::ComponentCondition::Always); setuptext(FgC(Hover), UI::ComponentCondition::Hover); setuptext(FgC(Down), UI::ComponentCondition::Down); setuptext(FgC(Disabled), UI::ComponentCondition::Disabled); - + { auto &caret = temp.AddGraphics(8, UI::ComponentCondition::Focused); caret.Content.SetAnimation(A(Caret)); @@ -2792,7 +2827,7 @@ caret.SetSizing(caret.Fixed); caret.SetSize(A(Caret).GetSize()); } - + { auto &selection = temp.AddGraphics(7, UI::ComponentCondition::Focused); selection.Content.SetAnimation(A(Rectangle, Selection, None, 0)); @@ -2803,17 +2838,17 @@ selection.SetSize(0, objectheight); selection.SetSizing(UI::ComponentTemplate::Fixed); } - + auto &vst = operator[](Scrollbar_Vertical); auto &hst = operator[](Scrollbar_Horizontal); - + temp.SetSize(temp.GetWidth() + vst.GetWidth() + spacing, temp.GetHeight()); - + boxed .AddIndex(9) //VScroll .AddIndex(10) //HScroll ; - + auto &vs = temp.AddPlaceholder(9, UI::ComponentCondition::VScroll); vs.SetTemplate(vst); vs.SetTag(UI::ComponentTemplate::VScrollTag); @@ -2821,7 +2856,7 @@ vs.SetSizing(UI::ComponentTemplate::Fixed); vs.SetAnchor(UI::Anchor::TopRight, UI::Anchor::TopRight, UI::Anchor::TopLeft); vs.SetMargin(spacing, 0, 0, 0); - + auto &hs = temp.AddPlaceholder(10, UI::ComponentCondition::HScroll); hs.SetPositioning(UI::ComponentTemplate::Absolute); hs.SetTemplate(hst); @@ -2830,8 +2865,8 @@ hs.SetSizing(UI::ComponentTemplate::Fixed); hs.SetAnchor(UI::Anchor::None, UI::Anchor::BottomCenter, UI::Anchor::BottomCenter); hs.SetMargin(0, spacing, vst.GetWidth()+spacing, 0); - - + + auto &contenthscroll = temp.AddContainer(5, UI::ComponentCondition::HScroll) .AddIndex(6) //text .AddIndex(7) //selection @@ -2843,7 +2878,7 @@ contenthscroll.SetTag(UI::ComponentTemplate::ViewPortTag); contenthscroll.SetIndent(0, 0, 0, hst.GetHeight()+spacing); - + return temp; }
--- a/Source/Gorgon/Widgets/Generator.h Thu Sep 23 12:15:33 2021 +0300 +++ b/Source/Gorgon/Widgets/Generator.h Tue Sep 28 07:31:52 2021 +0300 @@ -95,6 +95,9 @@ virtual UI::Template ColorPlane() = 0; virtual UI::Template ColorPicker() = 0; + + + virtual UI::Template TabPanel() = 0; virtual UI::Template Textarea() = 0; @@ -167,6 +170,8 @@ return *new UI::Template(ColorPlane()); case ColorPicker_Regular: return *new UI::Template(ColorPicker()); + case TabPanel_Regular: + return *new UI::Template(TabPanel()); case Textarea_Regular: return *new UI::Template(Textarea()); default: @@ -631,8 +636,11 @@ virtual UI::Template ColorPlane() override; virtual UI::Template ColorPicker() override; - - + + + virtual UI::Template TabPanel() override; + + virtual UI::Template Textarea() override;
--- a/Source/Gorgon/Widgets/Registry.h Thu Sep 23 12:15:33 2021 +0300 +++ b/Source/Gorgon/Widgets/Registry.h Tue Sep 28 07:31:52 2021 +0300 @@ -62,6 +62,8 @@ ColorPlane_Regular, ColorPicker_Regular, + + TabPanel_Regular, Textarea_Regular,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Gorgon/Widgets/TabPanel.h Tue Sep 28 07:31:52 2021 +0300 @@ -0,0 +1,248 @@ +#pragma once + +#include "Composer.h" +#include "../Containers/Hashmap.h" +#include "../Containers/Collection.h" + +#include "Panel.h" + +namespace Gorgon { namespace Widgets { + template <class Key_> + class basic_TabPanel; + + /** + * This class represents a tab in Tab TabPanel + */ + template<class Key_> + class Tab : public Panel { + friend class basic_TabPanel<Key_>; + public: + //TODO index, key, text, icon + //blankpanel + + private: + Tab(basic_TabPanel<Key_> &parent, const UI::Template &temp) : Panel(temp), parent(&parent) { + } + + basic_TabPanel<Key_> *parent; + }; + + /** + * This widget is a tabbed container. While the widget itself is not + * a container, its members are. Each tab is associated with a key. + * Creating another with the same key will erase the tab, removing its + * contents. + */ + template <class Key_> + class basic_TabPanel : public ComponentStackComposer { + public: + + /// Enumeration that controls how tab buttons are sized + enum ButtonSizing { + /// Size is automatic depending on the title length + Auto, + /// Size is the smallest integer unit size that the title will fit into + AutoUnit, + /// Only the given size is used + Fixed, + /// Fills the full width of the tab bar + Fill, + /// Fills the full width of the container but will the given size + /// is used as maximum + Adaptive, + }; + + /// Enumeration that controls how the overflow in buttons is treated + enum ButtonOverflow { + /// Any excess buttons will be hidden + HideExcess, + /// Buttons can be scrolled vertically + VScroll, + /// Buttons can be scrolled horizontally + HScroll, + /// Buttons are shown in multiple lines + ExpandLines, + /// Buttons are forced to a scale that will fit the bar + Scale, + /// Excess buttons are grouped in a list + List + }; + + /// Construct a new panel + explicit basic_TabPanel(const UI::Template &temp) : ComponentStackComposer(temp) { + } + + /// Construct a new panel + explicit basic_TabPanel(Registry::TemplateType type = Registry::TabPanel_Regular) : basic_TabPanel(Registry::Active()[type]) { + } + + /// Create a new tab with the given key and title + Tab<Key_> &New(const Key_ &key, const std::string &title); + + /// Create a new tab with the given key, title will be determined from the key + Tab<Key_> &New(const Key_ &key) { + return New(key, String::From(key)); + } + + /// Create a new tab with the given title, key will be determined from the title + template<typename K_ = Key_> + typename std::enable_if<!std::is_same<K_, std::string>::value, Tab<Key_> &>::type + New(const std::string &title) { + return New(String::To<Key_>(title), title); + } + + /// Create a new tab with the given key and title. The tab will be inserted before the given key. + /// If the key does not exist, new tab will be appended to the end. + Tab<Key_> &Insert(const Key_ &before, const Key_ &key, const std::string &title); + + /// Create a new tab with the given key, title will be determined from the title. The tab will be + /// inserted before the given key. If the key does not exist, new tab will be appended to the end. + Tab<Key_> &Insert(const Key_ &before, const Key_ &key) { + return Insert(before, key, String::From(key)); + } + + /// Create a new tab with the given title, key will be determined from the title. The tab will be + /// inserted before the given key. If the key does not exist, new tab will be appended to the end. + template<typename K_ = Key_> + typename std::enable_if<!std::is_same<K_, std::string>::value, Tab<Key_> &>::type Insert(const Key_ &before, const std::string &title) { + return Insert(before, String::To<Key_>(title), title); + } + + /// Remove the tab at the given key + void Remove(const Key_ &key); + + /// Remove all tabs with the supplied title + void RemoveAllOf(const std::string &title); + + /// Moves the tab at the given key before another tab. If the before tab does not exit, the tab + /// will be moved to the end. + void MoveBefore(const Key_ &before, const Key_ &tab); + + /// Return the tab with the supplied key + Tab<Key_> &operator [](const Key_ &key) { + return mapping[key]; + } + + /// Return the tab with the supplied key + const Tab<Key_> &operator [](const Key_ &key) const { + return mapping[key]; + } + + /// Returns true if the tab with the given key exist + bool Exists(const Key_ &key) const { + return mapping.Exists(key); + } + + bool Activate() override { + return true; + } + + /// Activates the tab with the key. If key does not exist nothing is done. + void Activate(const Key_ &key); + + /// Deactivate the currently active tab. + void Deactivate(); + + /// Activates the next tab + void ActivateNext(); + + /// Activates the previous tab + void ActivatePrevious(); + + /// Returns if there is an active tab. + bool HasActiveTab() const; + + /// Returns the currently active tab. Throws is there is no active tab. + Tab<Key_> &GetActiveTab(); + + /// Returns the currently active tab. Throws is there is no active tab. + const Tab<Key_> &GetActiveTab() const; + + /// Set how the tab buttons will be resized. Default is AutoUnit + void SetButtonSizing(ButtonSizing value); + + /// Returns how the tab buttons will be resized + ButtonSizing GetButtonSizing() const { + return sizing; + } + + /// Sets if the button text would be wrapped. If true, current tab size + /// will be used to determine wrap width. Default value is false. + void SetButtonTextWrap(bool value); + + /// Returns if the button text would wrapped. + bool GetButtonTextWrap() const { + return buttontextwrap; + } + + /// Sets how the overflowing tab buttons are managed. Default is Scale. + /// Some options are not implemented yet and will default to Scale. + void SetButtonOverflow(ButtonOverflow value); + + /// Returns how the overflowing tab buttons are managed. + ButtonOverflow GetButtonOverflow() const { + return overflow; + } + + /// Sets the size of the tab buttons. The size will be used according + /// to ButtonSizing. + void SetButtonSize(int w, int h) { + SetButtonSize({w, h}); + } + + /// Sets the size of the tab buttons. The size will be used according + /// to ButtonSizing. + void SetButtonSize(const Geometry::Size &size); + + /// Returns the size of the buttons + Geometry::Size GetButtonSize() const { + return buttonsize; + } + + /// Sets if next or previous tab switches will rollover. Default is false + void SetTabRollover(bool value); + + /// Returns if next or previous tab switches will rollover. + bool GetTabRollover() const { + return rollover; + } + + + /// Used to enumerate tabs + auto begin() { + return tabs.begin(); + } + + + /// Used to enumerate tabs + auto begin() const { + return tabs.begin(); + } + + /// Used to enumerate tabs + auto end() { + return tabs.end(); + } + + /// Used to enumerate tabs + auto end() const { + return tabs.end(); + } + + private: + //for key mapping + Containers::Hashmap<Key_, Tab<Key_>> mapping; + + //ordered list + Containers::Collection<Tab<Key_>> tabs; + + ButtonSizing sizing = AutoUnit; + bool buttontextwrap = false; + ButtonOverflow overflow = Scale; + Geometry::Size buttonsize; //constructor will initialize to 3x1U + bool rollover = false; + }; + + using TabPanel = basic_TabPanel<std::string>; + +} }
--- a/Source/Gorgon/Widgets/dir.cmake Thu Sep 23 12:15:33 2021 +0300 +++ b/Source/Gorgon/Widgets/dir.cmake Tue Sep 28 07:31:52 2021 +0300 @@ -53,6 +53,8 @@ DialogWindow.h DialogWindow.cpp + + TabPanel.h Textarea.h Textarea.cpp
--- a/Testing/Source/Manual/UI_WidgetTest.cpp Thu Sep 23 12:15:33 2021 +0300 +++ b/Testing/Source/Manual/UI_WidgetTest.cpp Tue Sep 28 07:31:52 2021 +0300 @@ -1,12 +1,10 @@ #include "GraphicsHelper.h" -#include <Gorgon/UI/ComponentStack.h> #include <Gorgon/Widgets/Generator.h> #include <Gorgon/UI/Window.h> #include <Gorgon/UI/Organizers/Flow.h> #include <Gorgon/Widgets/Window.h> -#include <Gorgon/Widgets/Textarea.h> -#include <Gorgon/Widgets/Checkbox.h> +#include <Gorgon/Widgets/TabPanel.h> std::string helptext = "Key list:\n" @@ -20,18 +18,11 @@ auto &org = app.wind.CreateOrganizer<UI::Organizers::Flow>(); - Widgets::Textarea wgt1("Hello world\nI am a text area\n1\n2\n3\n4\n5"); - Widgets::Checkbox wrap("Wrap", true); - wrap.ChangedEvent.Register([&]{ - wgt1.WordWrap = (bool)wrap; - }); + Widgets::TabPanel wgt1; org - << wrap << org.Break << wgt1 ; - wgt1.SetText(wgt1.GetText() + "A lot more text is written in here..."); - app.wind.Run();