Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: SFGUI (0.4.0 released)  (Read 391909 times)

0 Members and 2 Guests are viewing this topic.

theo

  • Newbie
  • *
  • Posts: 21
    • View Profile
Re: SFGUI (0.3.2 released)
« Reply #630 on: July 19, 2017, 01:30:53 pm »
In the meantime I have been doing something else, and run into another problem.

I'm trying to make a wizard-like sequence of GUI screens. You fill in one screen, click "next" and get the next screen. I use the approach described here (https://github.com/SFML/SFML/wiki/Tutorial%3A-Manage-different-Screens) for going from screen to screen.

The problem I observe, is that when I go from screen 1 to screen 2, I see both screens through each other, transparent-like. What I suspect is happening is that both screens have their own sfg::Window object and both are being displayed. The transparancy can be due to the style chosen. My time for this project is little and fragmented (really lost moments), so I have not been able yet to experiment exhaustively.

My question is, what is the best approach for doing what I attempt to do.
- use one sfg::Window and one sfg::Desktop and propagate them through all screens of the wizard. I guess I will have to remove all widgets from the sfg::Window object when going to the next screen. Since RemoveAll doesn't seem to be recursive, this means I will have to keep pointers to all containers, and clean them in the right order.
- at the end of each screen close the sfg::Window and the sfg::Desktop and open new ones in the next screen. Since I still have the SFML RenderTarget, the application window should survive this (does it?). This way, I won't have to traverse the widget hierarchy, but I do get the overhead of opening new sfg::Window and sfg::Desktop objects for each screen.

Another question: is there any hope that the SFGUI website will be back in the forseeable future? Now there is no documentation at all on using themes. On the website there was not much, but at least there was something. Also the forum dedicated to SFGUI was nice and instructive.

I am not an experienced GUI developer. I am a programmer, but normally I do backend stuff. So maybe I miss some insight on how these things are normally done.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: SFGUI (0.3.2 released)
« Reply #631 on: July 19, 2017, 10:11:30 pm »
I haven't fully followed your posted code, but from your description, the better way to implement this, and a way that should somewhat fix your problem, is to separate the UI and the stats value. Meaning that the stats shouldn't be determined based on what is shown on the UI, but that the UI updates based on the values held by some class/struct.
So if you want to change multiple values, you just update the stats class value and then update the affected UI elements (or just all UI elements). If a UI refresh happens multiple times then that's not really an issue, as a refresh doesn't change the values.

If your problem however is due to a bug, then that should of course be fixed.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: SFGUI (0.3.2 released)
« Reply #632 on: July 19, 2017, 10:17:37 pm »
As for your second problem, it depends a bit how you want to use things. Since SFGUI unfortunately uses a lot of shared_ptr, thus as long as you have a reference to one element it will stay alive.
So if screen A and screen B each have their own window, both are instantiated and you both register them with SFGUI, they both will be drawn.
As for desktops, not sure if multiple ones make sense.

I'll poke Tank to hurry up with moving the website. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

theo

  • Newbie
  • *
  • Posts: 21
    • View Profile
Re: SFGUI (0.3.2 released)
« Reply #633 on: July 21, 2017, 12:00:33 pm »
Thanks for your responses.

First question: yes, that sounds like a good idea. I got another idea too: split the value of attributes into subvalues: base, bought, bonus. Only "bought" is a spinbutton, the others are regular labels. If the user buys ST, it increases the "bought" subvalue of ST and the "bonus" subvalue of HP. In this system, the values of the interactive widgets don't have to be changed programmatically. And also, the accounting of CP becomes easier. Cost has to be computed only for "bought" attribute points. That makes it both easier for the program to recalculate all values, and for the user to keep track of where his CP budget goes to. It also makes it easier to keep track of free (temporary) bonuses caused by spells, magic items etc. They al go to "bonus" and that way they are not included in calculating the CP cost.

Second question: yes I keep member variables pointing to widgets, so they are easily accessible from the callback functions. Those references will prevent the widgets from being destroyed. That will probably be the reason why they stay visible. This is easily fixed by setting the member variables to nullptr when the widget is no longer needed.

Also for this I have thought up a slightly different approach. The different screen objects stay alive in the screen manager object. I guess it is more efficient to just keep the windows intact, but only make the active screen visible and sensitive. That way it's easy to make a back button that keeps the choices the user made beforehand: just switch the visibility and sensitivity.
So it will work like this:
- the screen manager keeps the SFGUI and desktop objects, they are passed by reference to each screen object in the run () function. Don't want the compiler to copy them. So the run function has three parameters, all references: the SFML rendertarget, sfg::SFGUI and sfg::Desktop.
- each screen has its own sfg::Window object. The GUI layout (the widget hierarchy) is build up in the constructor of the screen object.
- if run () is called by the manager, make the current sfg::Window active: guiwindow -> SetState (sfg::Widget::State::NORMAL); and guiwindow -> Show (true);
- if "next" or "back" button is clicked, just make the current window inactive and invisible: guiwindow -> SetState (sfg::Widget::State::INSENSITIVE); and guiwindow -> Show (false);. Now the manager will call the run () of the appropriate next window. Will making the window INSENSITIVE also make all underlying widgets INSENSITIVE? Otherwise there may remain invisible hot zones on the screen, which is not what I want.
- if the wizard is left altogether, reset all widgets to default value, so the next time the user has a clean start.

Does this approach make sense?

NeroGames

  • Full Member
  • ***
  • Posts: 101
  • Build games is simple
    • View Profile
    • Nero Game Engine
    • Email
Re: SFGUI (0.3.2 released)
« Reply #634 on: July 21, 2017, 04:08:54 pm »
Hi Theo !, i've done a program that use several windows with SFGUI (check out the video), if it's what you want, i may help you. I've done the program long time ago, so i'll need some time to check out the source code and review how i've done it.

« Last Edit: July 21, 2017, 04:12:34 pm by Sk Landry »

NeroGames

  • Full Member
  • ***
  • Posts: 101
  • Build games is simple
    • View Profile
    • Nero Game Engine
    • Email
Re: SFGUI (0.3.2 released)
« Reply #635 on: July 24, 2017, 01:08:20 am »
Hi there! the method i've used for my program is the following : when i go to one screen to another, i destroy the current screen and create the next. So there is always only one screen in the program. Most of the data is stored in a SQLite database, each time a screen is created the data may be load from there.

Since screens are created at each transition, you may notice that the transition take several seconds.

Concretely i use two classes.
  • State : each screen inherite from this class
  • StateStack : manage the transition between screens

each time a screen is created (at each transition) i reinitialize the SFML Window GL state, the SFGUI SFGUI instance and the SFGUI Desktop instance

Quote
context.window->resetGLStates();
context.sfgui = &mSfgui;
context.desktop->RemoveAll();
« Last Edit: July 24, 2017, 01:15:46 am by Sk Landry »

Parsley

  • Newbie
  • *
  • Posts: 13
    • View Profile
Re: SFGUI (0.3.2 released)
« Reply #636 on: July 25, 2017, 09:08:26 am »
Hi,

I'm having trouble figuring out how to show/hide multiple sfg::Desktop in the same sf::RenderWindow. Is it possible in the current version of SFGUI? Here is a modified version of the sfgui desktop example, where I have put the second sfg::Window in a separate desktop:

Quote
#include <SFGUI/SFGUI.hpp>
#include <SFGUI/Widgets.hpp>

#include <SFML/Graphics.hpp>
#include <sstream>

class DesktopExample {
   public:
      DesktopExample();

      void Run();

   private:
      static const unsigned int SCREEN_WIDTH;
      static const unsigned int SCREEN_HEIGHT;

      void OnCreateWindowClick();
      void OnDestroyWindowClick();
      void OnFrontClick();

      // Create an SFGUI. This is required before doing anything with SFGUI.
      sfg::SFGUI m_sfgui;

      sfg::Desktop m_desktop;
      sfg::Desktop m_desktop2;
      sfg::Window::Ptr m_window;
      unsigned int m_count;

      bool m_is_second;
};

const unsigned int DesktopExample::SCREEN_WIDTH = 800;
const unsigned int DesktopExample::SCREEN_HEIGHT = 600;

int main() {
   DesktopExample app;
   app.Run();

   return 0;
}

DesktopExample::DesktopExample() :
   m_desktop(),
   m_desktop2(),
   m_window( sfg::Window::Create() ),
   m_count( 0 ),
   m_is_second(false)
{
}

void DesktopExample::Run() {
   sf::RenderWindow render_window( sf::VideoMode( SCREEN_WIDTH, SCREEN_HEIGHT ), "SFGUI Desktop Example" );
   sf::Event event;

   // We have to do this because we don't use SFML to draw.
   render_window.resetGLStates();

   // Init.
   m_desktop.SetProperty( "Button#create_window", "FontSize", 18.f );

   //// Main window ////
   // Widgets.
   m_window->SetTitle( "SFGUI Desktop Example" );

   auto intro_label = sfg::Label::Create( "Click on \"Create window\" to create any number of new windows." );
   auto create_window_button = sfg::Button::Create( "Create window" );
   create_window_button->SetId( "create_window" );

   // Layout.
   auto main_box = sfg::Box::Create( sfg::Box::Orientation::VERTICAL, 5.f );
   main_box->Pack( intro_label, false );
   main_box->Pack( create_window_button, false );

   m_window->Add( main_box );
   m_desktop.Add( m_window );

   // Signals.
   create_window_button->GetSignal( sfg::Widget::OnLeftClick ).Connect( std::bind( &DesktopExample::OnCreateWindowClick, this ) );

   while( render_window.isOpen() ) {
      while( render_window.pollEvent( event ) ) {
         if(
            (event.type == sf::Event::Closed) ||
            (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape)
         ) {
            render_window.close();
         }
         else {
                if(m_is_second){
                    m_desktop2.HandleEvent( event );
                }
                else
                {
                    m_desktop.HandleEvent( event );
                }

         }
      }

      m_desktop2.Update( 0.f );
      m_desktop.Update( 0.f );

      render_window.clear();
      m_sfgui.Display( render_window );
      render_window.display();
   }
}

void DesktopExample::OnCreateWindowClick() {
   ++m_count;

   m_is_second = true;

   // Create a new window.
   auto window = sfg::Window::Create();

   std::stringstream sstr;
   sstr << "A new window (" << m_count << ")";
   window->SetTitle( sstr.str() );

   // Widgets.
   auto destroy_button = sfg::Button::Create( "Destroy" );
   auto front_button = sfg::Button::Create( "Main window to front" );

   // Layout.
   auto box = sfg::Box::Create( sfg::Box::Orientation::VERTICAL, 5.f );
   box->Pack( sfg::Label::Create( "This is a newly created window, from runtime, interactively." ), false );
   box->Pack( sfg::Label::Create( "You can move me around, try it!" ), false );
   box->Pack( sfg::Label::Create( "Or click the button below to destroy me. :-(" ), false );
   box->Pack( destroy_button, false );
   box->Pack( front_button, false );

   window->Add( box );
   m_desktop2.Add( window );

   // Signals.
   destroy_button->GetSignal( sfg::Widget::OnLeftClick ).Connect( std::bind( &DesktopExample::OnDestroyWindowClick, this ) );
   front_button->GetSignal( sfg::Widget::OnLeftClick ).Connect( std::bind( &DesktopExample::OnFrontClick, this ) );
}

void DesktopExample::OnDestroyWindowClick() {
    m_is_second = false;
   // Obtain parent window.
   auto widget = sfg::Context::Get().GetActiveWidget();

   while( widget->GetName() != "Window" ) {
      widget = widget->GetParent();
   }

   // Remove window from desktop.
   m_desktop2.Remove( widget );
}

void DesktopExample::OnFrontClick() {
   m_desktop.BringToFront( m_window );
}

As you can see, I can make it so the first desktop is not interact-able when the second window is created in the second desktop by using:

Quote
                if(m_is_second){
                    m_desktop2.HandleEvent( event );
                }
                else
                {
                    m_desktop.HandleEvent( event );
                }

But the "SFGUI Desktop Example" window (contained in the first desktop) is still visible. Is there a way to hide the first desktop?

I know I can use hide/show on the sfg::Window, but is there a way to hide/show the desktop itself? I don't want to RemoveAll() the widgets from the first desktop either, as in my app the second desktop is an in-game menu. So the player would briefly visit this second desktop and then quickly return to the first desktop in its current state.
« Last Edit: July 25, 2017, 09:14:53 am by Parsley »

theo

  • Newbie
  • *
  • Posts: 21
    • View Profile
Re: SFGUI (0.3.2 released)
« Reply #637 on: July 26, 2017, 05:03:11 pm »
Hi Landry,

I have solved the problem by keeping all window objects, and just switching the sensitive/visible states. It works perfectly. Making the window insensitive, makes all widgets in the window insensitive, so there is no weird behaviour at all.

I pushed the responsibility of switching state into the manager. So the manager keeps track of the previous window and the next one. Prev is set equal to next at the top of the loop. Next is made active (sensitive and visible). Now the screen is run, the return value is captured in next. Prev is made inactive and the loop repeats. Easy peasy.

The good thing is, that all windows are present in the manager, so switching is really fast. I guess this will turn into a bad thing if there are many windows, or the windows become big in memory footprint. But for a simple wizard, it's perfect. Also, if the user uses the "back" navigation key, he gets the previous screen exactly as he left it. No need to retype text entries, or to reselect many options.

I also base the window layout on configuration in a SQLite database, but that is not  supposed to be dynamic, so not reloading it is not a problem.

theo

  • Newbie
  • *
  • Posts: 21
    • View Profile
Re: SFGUI (0.3.2 released)
« Reply #638 on: July 26, 2017, 05:04:36 pm »
@eXpl0it3r

Thanks a lot for your replies, it has been really helpful. I just needed a lightbulb moment and you have provided that.

theo

  • Newbie
  • *
  • Posts: 21
    • View Profile
Re: SFGUI (0.3.2 released)
« Reply #639 on: July 27, 2017, 03:41:11 pm »
@Parsley:

I don't think that using more than one desktop in SFGUI will work. See the second reply of eXpl0it3r to me:

Quote
As for desktops, not sure if multiple ones make sense.

I guess there are several basic approaches for traversing from one SFGUI screen to the next:

- keep all screens in memory and make sure only one is visible and sensitive at any given time (my approach)
- keep one window and on a navigation event empty it with RemoveAll and fill it again with the widget hierarchy of the next logical screen: you effectively reuse the sfg::Window object.
- on a navigation event destroy the sfg::Window and construct the sfg::Window of the next screen (Landry's approach)

What works best for your situation depends on your project: if the screens are lightweight (no big background graphics etc) and static, my approach works well. If the layout of the screens is dynamic and can be different every time depending on data, or the screens have a big memory footprint,  Landry's approach will work well.

I don't know how expensive destroying and reconstructing sfg::Window objects is. I don't expect that to be a dealbreaker, but if screen transitions become slow, it's probably worth the effort to try reusing it.

Note: I'm not an SFGUI developer, so take my remarks for what they're worth.

Parsley

  • Newbie
  • *
  • Posts: 13
    • View Profile
Re: SFGUI (0.3.2 released)
« Reply #640 on: July 27, 2017, 04:28:05 pm »
Hi Theo,

Thanks for your suggestions. My problem is that in my app I'm trying to keep the first desktop in its current state (of which windows are showing and which are hidden) while the user is interacting with the second desktop. Then when the user is done with the second desktop, they can go back to the first desktop with only the windows they already had open showing (I have about 30-40 windows in the first desktop - it's a big program).

I'm experimenting with adding something like below to desktop.hpp, but it's not quite working yet:

Quote
      WidgetsList m_hidden;

      void HideDesktop(){
            WidgetsList::iterator itD = m_children.begin();
            while(itD != m_children.end()){
                if((*itD)->IsGloballyVisible()){ //if currently visible, hide it
                    m_hidden.push_back(*itD);
                    (*itD)->Show(false);
                }

                ++itD;
            }
      }

      void ShowDesktop(){
            WidgetsList::iterator itD = m_children.begin();
            while(itD != m_children.end()){
                WidgetsList::iterator itW = m_hidden.begin();
                while(itW != m_hidden.end()){
                    if(*itD == *itW){ //If found in hidden list
                        (*itD)->Show(true);
                    }
                }

                ++itD;
            }
            m_hidden.clear(); //All showing so none should be hidden
      }


Parsley

  • Newbie
  • *
  • Posts: 13
    • View Profile
Re: SFGUI (0.3.2 released)
« Reply #641 on: July 29, 2017, 02:08:31 pm »
Ok I found another way of doing what I wanted. I keep a vector of all the windows I've created in the main desktop, and when I go to look at the second desktop I hide all the visible windows and store the ones I've hidden in a separate list. Then when it's time to unhide them, I go through that list and call Show(true):

Quote
        std::vector<sfg::Window::Ptr> m_allWindows;
        std::vector<sfg::Window::Ptr> m_hiddenWindows; //For storing which ones were visible

void HideAllVisibleWindows()
{
    std::vector<sfg::Window::Ptr>::iterator itW = m_allWindows.begin();
    while(itW != m_allWindows.end())
    {
        if((*itW)->IsGloballyVisible())
        {
            (*itW)->Show(false);
            m_hiddenWindows.push_back(*itW);
        }

        ++itW;
    }
}

void ShowAllVisibleWindows()
{
    std::vector<sfg::Window::Ptr>::iterator itW = m_hiddenWindows.begin();
    while(itW != m_hiddenWindows.end())
    {
        (*itW)->Show(true);

        ++itW;
    }
    m_hiddenWindows.clear();
}


Dehaku

  • Newbie
  • *
  • Posts: 2
    • View Profile
Re: SFGUI (0.3.2 released)
« Reply #642 on: October 19, 2017, 05:29:38 am »
Spent the past few hours digging, couldn't find it.
How do you change the color of the titlebar in windows and such?
Best I can do is backgrounds, borders, and highlights with SetProperty() on GetEngine()
If there's a list of widget properties I could look that, that would be immensely helpful and appreciated.

Edit: Label properties would be useful too, for changing their text color specifically.
« Last Edit: October 19, 2017, 08:30:01 am by Dehaku »

Parsley

  • Newbie
  • *
  • Posts: 13
    • View Profile
Re: SFGUI (0.3.2 released)
« Reply #643 on: October 19, 2017, 12:33:12 pm »
There used to be a reference page for widget properties but it's disappeared since they took the old website down. For the window titlebar the property is called 'TitleBackgroundColor'. Label text color I *think* the property is just called 'Color'.

FRex

  • Hero Member
  • *****
  • Posts: 1848
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: SFGUI (0.3.2 released)
« Reply #644 on: October 19, 2017, 01:13:04 pm »
Yes, see (some properties that affect sizes like font name, font size, etc. are in widget cpp files but most are in BREW cpp files that implement turning widgets into render lists or what not):
https://github.com/TankOs/SFGUI/blob/master/src/SFGUI/Engines/BREW/Window.cpp#L16
https://github.com/TankOs/SFGUI/blob/master/src/SFGUI/Engines/BREW/Label.cpp#L15
« Last Edit: October 19, 2017, 01:15:15 pm by FRex »
Back to C++ gamedev with SFML in May 2023

 

anything