Project

General

Profile

Problem filling the browser window -- WStackedWidget

Added by M H over 14 years ago

I intend to write an app that uses a stacked widget near the top of its hierarchy; the children of this widget corespond to different modes my app can be in, and each should take 100% of the screen area. I'm very new to Wt, so its quite likely I'm doing something very wrong ;) It's not working.

I've got an object that inherits Ext::Container, and represents one part of my application. If I instantiate this object as a child of the WApplication's root(), it displays exactly as I expect it to, full screen.

To advance to be able to display multiple such objects, I've started working with the stacked widget, but my success is only partial. Here's what the code looks like (the constructor of my class that inherits WApplication):

WStackedWidget *clientPanel = new WStackedWidget(root());
MyAppPanel *panel = new MyAppPanel (clientPanel); // MyAppPanel  inherits Ext::Container

At first I thought this was completely failing, as my screen is empty. [If I don't create clientPanel, and I instead create new MyAppPanel(root()), things work.] Then I realized, there's a tiny sliver of something at the top of the render area! So, I added a line of code:

panel->resize(100, 100);

and then I saw what's happening... the size isn't being set automatically (but evidentally, when not a child of WStackedWidget, it is?).

Ok, so I added this, not really thinking it would work (and I wasn't disappointed):

pane->resize(WLength(100.0, WLength::Percentage), WLength(100.0, WLength::Percentage));

I was a bit surprised to see this changed the panel size to 16 width, 16 height. A proxy size, I assume.

I suppose the right way to get this widget sized is with a layout object, and WFitLayout seems ideal; WBoxLayout (with only a single child) seems like it too should work. I've not been able to get either of these to work though. I've tried various combinations of using both layouts, and my results have ranged between seeing no effective visual change, to throwing an assert() [at WWidgetContainer.C line 121].

My platform is win32, and Wt 3.0.0. I'd appreciate any advice!

Thanks...

mah


Replies (5)

RE: Problem filling the browser window -- WStackedWidget - Added by Koen Deforche over 14 years ago

Hey,

Unfortunately, you will not be able to put an Ext-based layout manager inside a Wt-based layout manager, unless you explicitly set the size (width and height in pixels) on the container that contains the Ext layout manager. The opposite is possible (Wt-in-Ext).

Now, there would be a rather simple fix (I have tried it and it works) for this if either:

  • you only care about the height (you would still need to set the width in pixels)
  • you only care about static layout management: the layout does not need to adapt to changes

It could possibly also be fixed for the general case (but that would need to be verified especially in IE browsers).

What is the reason you want to use Ext ?

Regards,

koen

RE: Problem filling the browser window -- WStackedWidget - Added by M H over 14 years ago

Hi Koen,

My reason for leaning towards Ext is for the appearance... no technical reason. Dynamic layout in at least height is important to me; my overall layout is likely to be a border layout (with a small north and south area, and the center to be a stacked widget), and within the stacked widget, each panel will have another border layout, with east and/or west, and a center. In all cases, the center should be dynamically sized for both height and width, and the edges should be fixed along their primary axis and dynamic along the secondary. (I doubt I'm using clear terms... for east/west, I'm calling width=primary, and for north/south I'm calling height=primary.)

I've also been having another problem with the stacked widget --- when I change the current widget (using setCurrentIndex()) from within the application constructor, it works, but if I set it from within a slot, I see no change in my browser. I have a click event in one class emit a signal that the application object is connected to, and a std::cout in the slot verifies that it's getting reached, however the display doesn't change. Could this also be due to mixing Wt and Ext widgets, or maybe I'm doing something else wrong? I believe I could get around this by causing the event to redirect the browser to a different internal path, but I'd prefer the URL to remain fixed (and not let people jump through the history).

Thanks again...

mah

RE: Problem filling the browser window -- WStackedWidget - Added by Koen Deforche over 14 years ago

Hey,

So I managed to make some changes that make this work well.

Now the following code will have a panel that fills the window and reacts to resizes:

    WVBoxLayout *topLevelLayout = new WVBoxLayout(root());

    WStackedWidget *stack = new WStackedWidget();
    topLevelLayout->addWidget(stack);

    Ext::Panel *panel = new Ext::Panel();
    stack->addWidget(panel);
    panel->setTitle("Kiekeboe");

    WFitLayout *panelLayout = new WFitLayout();
    WContainerWidget *contents = new WContainerWidget();
    contents->setAttributeValue("style", "background-color: blue;");
    panelLayout->addWidget(contents);
    panel->setLayout(panelLayout);

I have done this in a development branch, I will back-port it to the master and push it.

The other problem, I would not have an idea. The stacked widget is a rather simple widget and often used. Could you narrow it down to a test case ?

Regards,

koen

RE: Problem filling the browser window -- WStackedWidget - Added by M H over 14 years ago

I wasn't able to get back to the point of being able to present my problem in a simple (or even the original) test case, however I have managed to simplify my test and build a small library of failures; it could be that I'm making errors in how I'm trying to use the library, but I don't see where anything I'm doing is clearly mistaken.

This test application consists of two classes, App and TestPanel. App is instantiated once and is the root() of the system. It creates a stacked widget, and then instantiates TestPanel twice, to become components of the stacked widget.

TestPanel creates a WLabel that is made to look like a link, and when clicked it emits a signal that App is connected to. App will use that signal to move to the next panel; App can toggle between panels 1 and 2 (or 0 and 1, if you prefer). The two instances of TestPanel are identical except for the text identifying which panel is rendered, and the int parameter in the signal emitted.

There is a #define TEST near the top of the code that can be used to decide which test, 0 through 7, to compile. Below it (and mostly with each test) are comments describing what I've seen when running that test.

This doesn't yet represent how I hope to be able to present my application, but I fear if I can't get things to work at such a low level I'll never get a real app going! (For what it's worth, I've been developing in Qt since version 2.3, but while I'm very comfortable with signals and slots, I've always used Qt Designer to lay out my windows, and that caused me to not need to learn some of what must be needed here.)

#include <iostream>
#include <Wt/WApplication>
#include <Wt/WContainerWidget>
#include <Wt/WEnvironment>
#include <Wt/WLabel>
#include <Wt/WStackedWidget>
#include <Wt/WServer>
#include <Wt/WText>

#include <Wt/WBorderLayout>
#include <Wt/WFitLayout>
#include <Wt/WHBoxLayout>

#include <Wt/Ext/Panel>

using namespace Wt;



//--------------------------------------------------------------------------
// TestPanel: this is an example of how the real panels (pages) will
// be built. In this test case, panels are identical except for their
// "next panel number".
class TestPanel : public WContainerWidget
{
public:
    TestPanel(WContainerWidget *parent, int nextPanelNumber);
    ~TestPanel();

    Signal<int>& changePanel() { return changePanel_; }


private slots:
    void NextLinkClicked();

private:
    int           nextPanelNumber_;
    Signal<int>   changePanel_;

};
//--------------------------------------------------------------------------



// define this to be the test number to try, [0,7]
#define TEST 0
// tests that work (but are too trivial): 0, 1
// tests that crash:                      2, 4, 7
// tests that show nothing:               6
// tests that only partially work:        3, 5



//--------------------------------------------------------------------------
TestPanel::TestPanel(WContainerWidget *parent, int nextPanelNumber)
    : WContainerWidget(parent),
      nextPanelNumber_(nextPanelNumber)
{
    char buf[128];

    sprintf(buf, "<font color='blue' style='cursor: hand;'><u>Next Page [%d]...</u></font>", nextPanelNumber);
    WLabel *registerLabel = new WLabel(buf);

    sprintf(buf, "Second widget, Next Page = %d", nextPanelNumber);
    WLabel *secondLabel = new WLabel(buf);

#if (TEST == 0) /* this works */
    addWidget(registerLabel);
    addWidget(secondLabel);
#endif

#if (TEST == 1)
    /* this works */
    WHBoxLayout *layout = new WHBoxLayout(this);
    layout->addWidget(registerLabel);
    layout->addWidget(secondLabel);
#endif

#if (TEST == 2)
    /* this causes an application abort when the browser opens the app:
       Assertion failed: false, file ..\..\src\Wt\WContainerWidget.C, line 121
    */
    WFitLayout *layout = new WFitLayout(this);
    layout->addWidget(registerLabel);
#endif


#if (TEST == 3)
    /* this works, but has a visual anomoly:
       When first loaded, the south panel is shown just below the center panel.
       When the link is clicked, the new page shows the south panel significantly
       lower than the center. When the link is clicked again, positions remain with
       this large delta.
       This duplicates under IE and Firefox
    */
    WBorderLayout *layout = new WBorderLayout(this);
    layout->addWidget(registerLabel, WBorderLayout::Center);
    layout->addWidget(secondLabel, WBorderLayout::South);
#endif


#if (TEST == 4)
    /* this causes an application abort when the browser opens the app:
       Assertion failed: false, file ..\..\src\Wt\Ext\Container.C, line 233
    */
    Ext::Panel *extPanel = new Ext::Panel(this);
    WHBoxLayout *layout = new WHBoxLayout(extPanel);
    layout->addWidget(registerLabel);
#endif

#if (TEST == 5)
    // this works however the first page is incompletely rendered... even though
    // the only differences between the two should be a single byte change (page number)
    WBorderLayout *layout = new WBorderLayout(this);

    Ext::Panel *north = new Ext::Panel();
    north->setLayout(new WFitLayout());
    north->layout()->addWidget(secondLabel);
    north->resize(WLength::Auto, 35);
    layout->addWidget(north, WBorderLayout::North);

    layout->addWidget(registerLabel, WBorderLayout::Center);
#endif


#if (TEST == 6)
    WBorderLayout *layout = new WBorderLayout(this);

    Ext::Panel *north = new Ext::Panel();
    north->setLayout(new WFitLayout());
    north->layout()->addWidget(secondLabel);
    north->resize(WLength::Auto, 35);
    layout->addWidget(north, WBorderLayout::North);

    Ext::Panel *center = new Ext::Panel();
    center->setLayout(new WFitLayout());
    center->layout()->addWidget(registerLabel);
    layout->addWidget(center, WBorderLayout::Center);
#endif

#if (TEST == 7)
    // similar to test 5, but a WContainerWidget instead of an Ext::Panel
    WBorderLayout *layout = new WBorderLayout(this);
    WContainerWidget *north = new WContainerWidget(0);
    north->setLayout(new WFitLayout());
    north->layout()->addWidget(secondLabel);
    north->resize(WLength::Auto, 35);
    layout->addWidget(north, WBorderLayout::North);

    layout->addWidget(registerLabel, WBorderLayout::Center);
#endif


    // install our slots
    registerLabel->clicked().connect(SLOT(this, TestPanel::NextLinkClicked));
}
//--------------------------------------------------------------------------





TestPanel::~TestPanel()
{
}

void TestPanel::NextLinkClicked()
{
    std::cout << "Next Page link was clicked...\n";
    changePanel_.emit(nextPanelNumber_);
}






//--------------------------------------------------------------------------
class App : public WApplication
{
public:
    App(const Wt::WEnvironment& env);
    ~App();

private slots:
    void changePanel(int panelNumber);

private:
    WStackedWidget *clientPanel_;

    TestPanel       *panel1_;
    TestPanel       *panel2_;
};
//--------------------------------------------------------------------------


App::App(const Wt::WEnvironment& env)
    : WApplication(env)
{
    setTitle("Testing");


    clientPanel_ = new WStackedWidget(root());

    panel1_ = new TestPanel(clientPanel_, 1);
    panel2_ = new TestPanel(clientPanel_, 0);

    panel1_->changePanel().connect(SLOT(this, App::changePanel));
    panel2_->changePanel().connect(SLOT(this, App::changePanel));
}
//--------------------------------------------------------------------------



App::~App()
{
}

void App::changePanel(int panelNumber)
{
    std::cout << "App::changePanel(" << panelNumber << ");\n";
    clientPanel_->setCurrentIndex(panelNumber);
    clientPanel_->currentWidget()->resize(1024, 256);
    clientPanel_->currentWidget()->show();
}


WApplication *createApplication(const WEnvironment& env)
{
    return new App(env);
}

int main(int argc, char **argv)
{

    // start the server loop
    try
    {
        // use argv[0] as the application name to match a suitable entry
        // in the Wt configuration file, and use the default configuration
        // file (which defaults to /etc/wt/wt_config.xml unless the environment
        // variable WT_CONFIG_XML is set)
        WServer server(argv[0]);

        // WTHTTP_CONFIGURATION is e.g. "/etc/wt/wthttpd"
        server.setServerConfiguration(argc, argv, WTHTTP_CONFIGURATION);

        server.addEntryPoint(Application, createApplication, "/page1");
        server.addEntryPoint(Application, createApplication, "/page2");

        if (server.start())
        {
            int sig = WServer::waitForShutdown();

            std::cerr << "Shutdown (signal = " << sig << ")" << std::endl;
            server.stop();
        }
    }

    catch (WServer::Exception& e)
    {
        std::cerr << e.what() << "\n";
        return 1;
    }

    catch (std::exception& e)
    {
        std::cerr << "exception: " << e.what() << "\n";
        return 1;
    }

}

RE: Problem filling the browser window -- WStackedWidget - Added by Koen Deforche over 14 years ago

M H wrote:

I wasn't able to get back to the point of being able to present my problem in a simple (or even the original) test case, however I have managed to simplify my test and build a small library of failures; it could be that I'm making errors in how I'm trying to use the library, but I don't see where anything I'm doing is clearly mistaken.

I've checked your various cases. Many of them due to unsupported combinations of WContainerWidget + layout manager, and Ext::Container + layout manager. Each layout manager documents which container support it.

TEST 2: You cannot use WFitLayout with WContainerWidget

see http://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WFitLayout.html

TEST 3: You need to indicate to the layout manager what it should manage (width and/or height).

In Wt, a layout manager will constrain the size of the widgets in the contents of the layout manager inside the container's space. This can only be done if the parent container has indeed a width and/or height which is constrained. In practice this means that the parent container needs to be the widget root(), a widget whose width and or height is explicitly set, or a widget that is contained in a layout manager.

In this case, the size of the parent is not constrained and the behavior therefore not defined. If only one aspect of the container is constrained (by the nature of the web/CSS, usually width is constrained to the viewport width, while height is unconstrained), you should specify how the layout manager should align relative to the container using e.g. container->setLayout(layout, AlignTop | AlignJustify) for unconstrained height but constrained width.

TEST 4: You cannot use WHBoxLayout in Ext::Container

see http://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WBoxLayout.html

TEST 5: cfr TEST 3

TEST 6: does not show nothing anymore with the latest git version

but also cfr TEST 3

TEST 7: cft TEST 2

Hope this clears up some of the problems ?

    (1-5/5)