Buttons 0.9

Started by billarhos, 26 May 2019, 08:26:21

billarhos


  • Buttons can blink, meaning that over a period of time can change background and text appearence. For instance can tongle between normal sprite and focused sprite. (global timer)
  • Every state (Normal, hover, etc) can have its own text color.
  • Every text for every state can have its own outline border.
  • Outline can have a thickness value.
  • Every text can have its own style (regular, bold).
  • Every text can have its own size.
  • Every text can have its own vertical and horizontal alignment on a given position inside the button (i can show you some button images if that is not clear).
  • A button can trigger (or not) from one or more keys. In my game i give the owner the ability to add any key he wants for every available button.
  • Having 9,there is no need of having "space" and "enter" as default keys out of the box.
  • Focusing in my opinion is over estimated but essential when you want to navigate with tab box. Bilinking over rules hovering and focusing.
  • Pressed time for "m_spriteDown" must be configurable. Action of triggering must be on the last frame. Zero value triggers the button at once showing one down frame.
  • Clicks or key presses can have "continuality" or not. Meaning that over pressing time, buttons can re-triggered as long as mouse or key is down.
  • A button can be tongle - switch button. (I saw you think for a different widget. It is not a bad idea having different widget. In my case is configurable (setTongle)).
  • Hovering can be disabled in touch screens because it is unnecessary (for all widgets).
  • Batch for rendering.

Those are some stuff that i have implemented in my codes over the years. Buttons and lists are the more demanding widgets in my opinion.

billarhos

Having different text position or and text size you can have very pretty pressed states. The text can grow or can be positioned a little downwards during the pressed event.

texus

Just wanted to let you know that I haven't forgotten about this yet, I just haven't had the time yet to go over the list in detail.

billarhos

Do not worry. All those need time for a proper discussion.



enum button_States
{
Normal,
Hover,
Down,
Disabled,
Focused
};

Sprite m_sprite[5];
Color   m_borderColor[5];
Color   m_backgroundColor[5];
Color    m_TextColor[5];

void setBackgroundColor(button_States state, Color color);
void setTextColor(button_States state, Color color);


Having a simple enum for all button states, you can simplify the code so you can have one function for all states.


texus

QuoteEvery state (Normal, hover, etc) can have its own text color.
QuoteEvery text can have its own style (regular, bold).
This is already possible in TGUI, so they don't need further discussion.

QuoteOutline can have a thickness value.
This will definitely be the case once I add support for outlines.

QuoteEvery text for every state can have its own outline border.
Do you mean outline color or also thickness? Colors I can agree with, but I feel like nobody would ever change the thickness per state even if I made it possible.

QuoteEvery text can have its own size.
Do you mean for every state? If yes then the text size would have to become part of the renderer. The problem with doing that is that the renderer would include a "size". My idea is to let the renderer contain stuff like colors and let the widget have any size that it wants (so e.g. item height and text size are part of the widget itself). Putting text sizes in the renderer means that a renderer is only suitable for widgets of certain sizes (e.g. a renderer with text size 12px is useless for a button of height 200px), which I want to avoid. So this might be a bit difficult to implement.

QuoteButtons can blink, meaning that over a period of time can change background and text appearence. For instance can tongle between normal sprite and focused sprite. (global timer)
This doesn't look very easy to implement as it requires a "global" state which overrides other focus/hover states. It might be better to implement this in user code. Of course it would be easier if the gui supported it directly, but it looks like a very niche feature. Same goes for other points below where I mention it can be done in your own code, it doesn't mean that it shouldn't be in the gui at all, just that it would get rather low priority.

QuoteEvery text can have its own vertical and horizontal alignment on a given position inside the button (i can show you some button images if that is not clear).
Could you show some examples of cases where not centering the text looks good?

QuoteA button can trigger (or not) from one or more keys. In my game i give the owner the ability to add any key he wants for every available button.
To me this sounds like something that should be done in user code. The user has full control over what keys are pressed and what he wants to do in these cases. I could make things easier by adding a function to Button that calls the connected signal handlers so that the user doesn't has to call them manually.
This probably can't even be implemented at the level of the button itself as key pressed are only passed when the widget is focused and I assume you also mean key presses when the button isn't focused. If you did mean only triggering on these keys when the button is focused then I would like to hear some examples of cases where you use other keys than space and return.

QuoteHaving 9,there is no need of having "space" and "enter" as default keys out of the box.
I actually want these things to work out of the box. I want TGUI to be as easy to use a possible, so things that most people want should be the default behavior.
The big problem is that I don't have a specific target audience for TGUI which means that it is hard to predict what most people would want and people use TGUI in very different situations and will want different behaviors.
If triggering on space and return remains the default then I do agree that there should be some way to disable the behavior for those that don't want it.

QuoteFocusing in my opinion is over estimated but essential when you want to navigate with tab box. Bilinking over rules hovering and focusing.
Being able to tab between widgets is the main reason why focusing even exists in TGUI. That and figuring out which edit box should get the key presses.
If blinking overrules focusing, then basically you can't do anything while something is blinking (except for e.g. pressing the blinking button)?

QuotePressed time for "m_spriteDown" must be configurable. Action of triggering must be on the last frame. Zero value triggers the button at once showing one down frame.
Do you mean that the down state should automatically jump to normal state even when keeping the mouse button pressed? Can you give some examples where this is used?

QuoteClicks or key presses can have "continuality" or not. Meaning that over pressing time, buttons can re-triggered as long as mouse or key is down.
This could be useful for implementing custom spin buttons where you have one button with a "-" and one button with a "+". Is that what you had in mind or did you know another use case for this?
Although for a spin button I feel like it should trigger immediately on mouse down, then after a "long" time trigger a second time and then each time after a "short" time trigger again. So that would be different from the "continuality", which wouldn't trigger immediately after mouse down? Or maybe it does trigger immediately on mouse down when this "continuality" setting is enabled?

QuoteA button can be tongle - switch button. (I saw you think for a different widget. It is not a bad idea having different widget. In my case is configurable (setTongle)).
I'm not sure how to best implement this, but this is the one thing on your list that I find the most important (which doesn't mean that it will be the first thing to be added though).

QuoteHovering can be disabled in touch screens because it is unnecessary (for all widgets).
Touch screen support is something that needs further investigation. TGUI was written as a desktop gui and I've never really used it on a touch screen (except for some brief tests with android), so some there are probably some other improvements that could be made as well. So maybe there should be some global toggle to change touch screen friendliness which would disable hovering for all widgets. The main problem would be to figure out whether a touch screen is used or not, but that would be up to the user. If the user doesn't explicitly asks to disable the hover states then I don't think I should make that decision in TGUI because I can't be certain that the device will only be used as a touch screen.

QuoteBatch for rendering.
Currently TGUI 0.9-dev is being designed such that all rendering has to go through a minimal interface. Drawing to the screen is only possible through this interface so everything that gets rendered will be batchable together with other widgets.

QuoteHaving different text position or and text size you can have very pretty pressed states. The text can grow or can be positioned a little downwards during the pressed event.
This would look nice but I fear that this is hard to do. The user would have to hardcode the text position in the renderer?

QuoteHaving a simple enum for all button states, you can simplify the code so you can have one function for all states.
That would indeed simplify things.
Ideally I would just have some properties that you can set directly instead of needing setters and getters but that will probably never work (because the widget needs to be notified about some changes such as border thickness and because I would like to be able to cache rendering in 0.9-dev which is only possible if I can detect that changes were made).
Given the huge amount of tunable things in the renderer I even considered having the property, setter and getter (both declaration and definition) generated with a define. The big issue with that is that I wouldn't be able to document the properties.

billarhos

#5
Let's disccus it one by one.

Quote
    Every text for every state can have its own outline border.

Quote
Do you mean outline color or also thickness? Colors I can agree with, but I feel like nobody would ever change the thickness per state even if I made it possible.

I mean both color and thickness.
Let's say we have a color text for every state. When you have white color for text and black color for outline color (normal state) is cool but when you use blue (pressed state) for text color and outline color is still black(1 outline color) is not so cool. We always look for a outline color that matches all text colors.

When you have a thickness value per state then you can easily make some cool effects without changing background color. (different text position inside button for every state also make things cool but this is next to our conversation)

When i say cool effects i mean pressing, hovering, disabling, focusing event can look awesome.



texus

For other widgets I'll keep the amount of out-of-the-box customizability with outlines limited, but I guess for buttons I can put some extra effort into it and allow a different outline thickness (and color) for every state. Buttons are probably the most used widgets after all.

Maybe I should also add FocusedHover and FocusedDown states? Right now you can't tell that a button is focused when it is being hovered.

billarhos

Maybe i am over reacting here with buttons and at the time i would like the best look of buttons maybe i ll push you into an overwork and more complicated source code. Cheer up. I have a brand new discussion list.

    1. Buttons can blink. Forget it. NO
    2. Every state (Normal, hover, etc) can have its own text color. This would look very nice if u use different sprite background colors. YES
    3. Every text for every state can have its own outline border. Keep one outline color. So NO.
    4. Outline can have a thickness value. Add one thickness value. So YES.
    5. Every text can have its own style (regular, bold). YES if possible.
    6. Every text can have its own size. No.
    7. Every text can have its own vertical and horizontal alignment on a given position inside the button. YES to position inside and YES to both alignments. (Have image)
    8. A button can trigger (or not) from one or more keys. In my game i give the owner the ability to add any key he wants for every available button. NO.
    9. Having 8,there is no need of having "space" and "enter" as default keys out of the box. NO AND YES. Default keys should be able to be disabled or not from a function.
    10. Focusing in my opinion is over estimated but essential when you want to navigate with tab box. Bilinking over rules hovering and focusing. FORGET IT.
    13. A button can be tongle - switch button. YES but i would propose it for a different widget so u can skip complexity.
    14. Hovering can be disabled in touch screens because it is unnecessary (for all widgets). YES AND NO. If i use same sprite for hovering no one understands if it is hovering. But i would be cool if i could disabled.
    15.Batch for rendering. YES

   11. Pressed time for "m_spriteDown" must be configurable. Action of triggering must be on the last frame. Zero value triggers the button at once showing one down frame.
   12. Clicks or key presses can have "continuality" or not. Meaning that over pressing time, buttons can re-triggered as long as mouse or key is down.

    In your code u wait for mouse up or key up event, correct?
   
   button->connect("MousePressed", [=]()
{
//mouse pressed event is triggering when mouse is up (after was down ok) correct?
});
   

   I ll come back on 11 and 12 if u answer my question.

billarhos

QuoteMaybe I should also add FocusedHover and FocusedDown states? Right now you can't tell that a button is focused when it is being hovered.

maybe my overreaction is contagious. lol. I think FocusedHover and FocusedDown states is too much. Hovering is just an effe. Correct? And is just for as long as mouse is on it.

i think priority order in rendering is: Disabled, Down, Hover, Focused, Normal.

texus

#9
QuoteIn your code u wait for mouse up or key up event, correct?
The pressed signal is triggered on mouse up.
For the space and return key the behavior turned out to be a bit less expected. It triggers on a key press event. Which means that if you currently hold the key it will trigger multiple times.

I was going to write that just like the Clicked signal the Pressed signal fires on mouse release, but that made me realize something: you can already "disable" the space and return keys. The difference between the Clicked and Pressed signal is exactly that: Clicked only fires when clicking the button while Pressed also fires when pressing the space or return key while the button is focused. So if you don't want to get a callback on space/return then you can just use the Clicked signal.

Edit: I just looked at how it happens in my browser. Buttons were triggered when releasing the space key but continuously when holding the return key. So maybe I should also have a look at how other libraries handle it.

billarhos

What happening if u call "mWindow.setKeyRepeatEnabled(false)". Still buttons trigering all the time via key down event?
I have never use key signals with tgui. I use the sfml's pollEvent  to trigger buttons via key presses aka shortcuts.

To clarify something. Space and enter do not have the same behaviour on buttons? if Yes how is this possible?

In my codes over the years i never used mouse up or key up events to fire an action on buttons. Instead i am using timers. (Or frame counters).
With counters-times i never worry about a thing. I can manipulate how quickly can be trigger an event
or if i want to do it repeatedly or once or even on up events just like u have it now in tgui.

texus

QuoteWhat happening if u call "mWindow.setKeyRepeatEnabled(false)". Still buttons trigering all the time via key down event?
Then it would only trigger once because the key press event would only be send once. It simply responds to the sf::Event::KeyPressed event that you get from SFML's pollEvent.

QuoteTo clarify something. Space and enter do not have the same behaviour on buttons? if Yes how is this possible?
In TGUI they do have the same behavior, but I tested it on a simple HTML button with both firefox and chrome and it looks like they really do have different behavior for space and return. (I tested it via https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_button_test where I replaced the onclick event with "console.log('test')"). While looking into the onSelect event for edit box recently I saw really weird behavior with HTML as well so I wouldn't want to blindly copy their behavior, but it does raise questions about what the most common behavior is.

My linux system seems to repeat for both space and return key, so it seems to work exactly like TGUI works now (with setKeyRepeatEnabled set to true).

QuoteIn my codes over the years i never used mouse up or key up events to fire an action on buttons.
As far as I can tell it is the most common way to fire on mouse up events, all buttons that I come in contact with seem to work that way.

billarhos

Quoteso it seems to work exactly like TGUI works now (with setKeyRepeatEnabled set to true)

Perhaps this must be optional.

QuoteAs far as I can tell it is the most common way to fire on mouse up events, all buttons that I come in contact with seem to work that way.

U half correct. There are plenty workcases that things works differenet. Like slot games or DJ programs for example. These are working in different way. Events are on down.
Sometimes are totally configurable like effect buttons both on dj program and on the dj controller (mechanical like pioneer).
a. Effect starts on down and play once.
b. Effect starts on down and play until mouse or key up.
c. Effect starts on down and playing until it presses again.

but all those stuff has nothing to do with TGUI apart from that the event is triggering on down action and not on up.
In my opinion triggering stuff is more important than anything else.

If u thinking of letting this to the user then perhaps u can add some signals like pressdown, pressup.


texus

QuotePerhaps this must be optional.
What should be optional exactly?

QuoteLike slot games or DJ programs for example.
Slot games and dj programs aren't the ones where I imagine people tabbing between buttons and using space to activate them, so you would only need mouse events. If you only need the mouse events (or touch events) then you can use the MousePressed signal to get a callback when the button is pressed down.

texus

So the following will definately be in 0.9-dev when it gets released:
- Every state can have a text color and text style (already in 0.8)
- Button can have an outline thickness and outline color (same for all states)
- Batch for rendering

Things to look into for 0.9-dev:
- Toggle and switch buttons
- Way to disabling hover state
- "Continuality" option
- Way to handling space and return


For the "continuality", how about a "setPressRepeat(sf::Time duration)" function? If set to 0 (default) then the button triggers on mouse up, but when a duration is set it would trigger the pressed signal on mouse down and again after every "duration".


While I was thinking about adding an enablePressBySpaceOrReturn function to disable space and return keys, I'm not sure if that would be the best way. I've looked online for questions where people ask to disable the space key and found the following behaviors:
- For javascript, the solution would be to either intercept the key event or to unfocus the button when you receive the focus event.
- For windows apps / .Net, the solution was to either intercept the key event or set IsTabStop to false to prevent the button from gaining focus.
- For unity, "Navigation" has to be set to "None", which as far as I can tell on first sight means not allowing it to be focused.
- Other random results came up where it was impossible to change the behavior or where it was suggested to unfocus the button after it gains focus.

So I think it might be better to add a function that prevents the button from gaining focus, instead of adding a function that disables space and return.


QuoteEvery text can have its own vertical and horizontal alignment on a given position inside the button. YES to position inside and YES to both alignments.
This is still the part that I find the most difficult to provide a generic solution for. I agree that the buttons you showed look great, but I'm not sure how to best give enough control to set the text position for buttons like that.

Maybe I shouldn't try to get the functionality in TGUI but to just make it easy enough to add it yourself? Imagine if you would be able to inherit from renderer classes and the ButtonRenderer would have a virtual function like this:
Code (c++) Select
void ButtonRenderer::drawText(RenderTarget& target, const Button* button, const FloatRect& rect, const String& text) const
{
    const TextProperties& textProperties{getCurrentTextColor(button), getCurrentTextStyle(button), m_textOutlineThickness, m_textOutlineColor};
    const FloatRect& bounds = target.calcTextBounds(text, textProperties);
    target.drawText(text, textProperties, {rect.left + (rect.width - bounds.width) / 2.f, rect.top + (rect.height - bounds.height) / 2.f});
}


All you would have to do is inherit from ButtonRenderer, override just the drawText function with your own implementation and call button->setRenderer(...) with an instance of your custom renderer.

billarhos

QuoteWhat should be optional exactly?
maybe i misunderstand something here. I thought that you using setKeyRepeatEnabled with true parameter inside TGUI but that's is not happening. Correct?

QuotesetPressRepeat(sf::Time duration)
that is how i imagined that. Great.

QuoteSo I think it might be better to add a function that prevents the button from gaining focus, instead of adding a function that disables space and return.
I don't mind at all. Having a disableFocusEvent function is also good enough for me.

QuoteEvery text can have its own vertical and horizontal alignment on a given position inside the button. YES to position inside and YES to both alignments.

First you can add alignment. Right now you have both center in button as default. You can add setHorizontalAlignment and setVerticalAlignment like label and half of the problem goes away. If additionally there is a fuction "setRelativeTextPosition(Layout x, Layout y)"  then you do not calculate the cente pos of button but you use this one obeying the alignements and letting this on user.


texus

Quotemaybe i misunderstand something here. I thought that you using setKeyRepeatEnabled with true parameter inside TGUI but that's is not happening. Correct?
TGUI doesn't call the function. I meant that the TGUI behavior would change if you called that function in your code and the button only works like described when key repeating is set to true (default).

QuoteYou can add setHorizontalAlignment and setVerticalAlignment like label and half of the problem goes away.
I feel like for buttons these functions belong in the ButtonRenderer class because they are about how a button will be rendered (unlike in Label where they are about how to align the label with other widgets), but I'll probably have to put them in the Button class. Functions like these really make me question if I wouldn't be able to find a better design to split widgets and their renderers.

I'm also not sure what do with the distance from the side yet. This is an issue that affects many widgets. If you put the text on the left side, do you expect it to start at pixel 0? This is how it looked in 0.7 and it looked terrible (because the black text touched the black border on the left). In 0.8 I added a small fixed margin between the side and the text to improve it. In 0.9-dev I was planning on making the margin even larger because I'm still not happy on how it looks with small texts. If you were to set setRelativeTextPosition(0,0) there would still be some space next to the text. This might be good because you probably don't really want the text against the side, but it might be unexpected as you are specifying (0,0). In this case adding a padding would solve the issue (although a default padding that isn't 0 is weird too), but in other widgets like ListView the distance is needed to keep the texts away from the column separators and a padding property isn't appropriate there (since there already is a padding but it goes around the entire list).
This is of course a whole different discussion, but depending on what you want when setting setRelativeTextPosition(0,0), the current implementation that placed a small space next to every text might be an issue.

billarhos

A. Now you center the text both vertical and horizontal in center of button, correct? So a "setMargin(value), getMargin()" make thing simpler.

B. Image show the proper use of setRelativePos, SetHorzontalAlign, SetVerticalAlign.

QuoteIf you were to set setRelativeTextPosition(0,0) there would still be some space next to the text
I can't see why you bother about the space next to text or even in front of it or up or down if someone sets new relative position?

texus

QuoteI can't see why you bother about the space next to text or even in front of it or up or down if someone sets new relative position?
Because the space could potentially be implemented in the Text class itself instead of having to add the margin in practically every widget. But 0.9 probably won't even have a Text class anymore, so we'll see how it turns out.

I've decided to not implement the text positioning now and first work on rewriting the core of the gui before further looking into it. One of the goals in 0.9 is to figure out how to make it easier for the user to implement custom customizations on top of what TGUI provides and I feel like I should try to get that in a working state before looking at which extra customizations can be provided out of the box.
Figuring out how to best allow the user position the text makes me think about other changes as well. I have similar "issues" with e.g. the Tab widget: how do I allow users to control where they put the text. If there was an icon in the tab then the text could be next to it, or below it or above it. There should be a distance to the sides of the tab and another distance between the text and icon. Both the icons and text should be sizeable as well. There is a huge amount of minor tweaks that the user might want to make and it is a lot of work to provide them. Just thinking about all the different customizations that I would have to provide makes me want to stop working on it :).

billarhos


texus

What exactly do you want to me to look at with that gui?
FairyGUI looks great, and they focus completely on games, 2 things you can't say about TGUI :)

I'm currently unsure how TGUI should continue in 0.9. There are just too many customizations to implement to really have a tunable gui (and I don't have the time to implement them). So I want to provide some way that the user can write their own code to extend the widgets. That way they can still do complex things (as I can't provide code for every scenario) while it would also be possible for me to sometimes merge the code back into TGUI to provide the functionality for everyone.

I'm completely stuck on a design for this though. The current design doesn't make it easy to extend the widgets at all, you would have to inherit both the widget and the renderer and override multiple functions. I was thinking about moving the draw function to the renderer so that most of the time you only need to inherit from the renderer. The issues with splitting the code in 2 part still remain though: mouse event functions and draw functions need to be equivalent, mouse press needs to know about borders, padding and spaces between items, which would all depend on the custom draw function.

Merging the widget and renderer into a single class again would simplify things and probably make it easier to extend it, but it probably makes it more difficult to implement skins. I tried looking into the code of some other guis but they usually have a complex structure. That is probably good for the extendability but I want to keep TGUI easy to use as well. It don't want to e.g. make it more difficult to have a simple list box with some strings just because someone may want to create a list box with subwidgets.

So instead of thinking about all that, I'm currently just spending my spare time playing borderlands 2, which is why I haven't responded for a few days :)

billarhos

QuoteWhat exactly do you want to me to look at with that gui?
Nothing really. Just to take a look. I haven't used fairygui yet but i was looking for an alternative among other libraries if some project of mine wanted advance features for gui. In my programming years i have use wxWidgets, QT and dotnet. However the last years since i abandoned "MARMALADE" (in fact they abandoned us in first place) i am using sfml and tgui which tgui is great since i never had any problems.

Take a breath.
Batch rendering is great but after the performance boost in latest versions is not completely necessary.
Instead of rewriting the whole project why don't you add the missing widgets.
Yesterday is saw default argument in "ignoreMouseEvents" (picture). Ok, why setEnabled or setVisible on the other hand don't have.
There are a lot of tiny improvements that can be done. Like having all those "signalNames" for all widgets in one place!
I know the worm of perfection since i have one but life is also fun..







texus

QuoteYesterday is saw default argument in "ignoreMouseEvents" (picture). Ok, why setEnabled or setVisible on the other hand don't have.
Because setEnabled and setVisible are equally likely to be called with true or false while you would only call ignoreMouseEvents with true (as false is the default and you typically don't toggle that property). That being said, I know that some function have default arguments while others don't where I can't make such a clear distinction. The problem is that I don't really like the default arguments as I feel like people might incorrectly see them as an indication of what the default is when the function isn't called and the default parameter value is usually the exact opposite of the default value used by the widget. So in more recent additions I didn't add a default argument at all.

QuoteInstead of rewriting the whole project why don't you add the missing widgets.
Because widgets and extra functions can always be contributed by others, nobody other than me is going to change the architecture of the entire gui.
I also tend to work in 2 phases, during development of a new version I can change the API so I try to change as much as possible at once while after the API gets more stable again I can focus on adding functionality that doesn't break the API (such as adding new widgets). By spending too much time in the unstable phase I always end up dividing the community in 2 versions (those using the older stable version and those using the newest unstable version), so I don't want this phase to last longer than 1-2 years. So things that can be done after tha API gets stable tend to get delayed during early development of a new version.

QuoteThere are a lot of tiny improvements that can be done. Like having all those "signalNames" for all widgets in one place!
This is hard to do with the inheritance. I'm also planning on a system like using the onSignal objects directly instead of using the signal names so it might get even more difficult to get things in one place.