Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-121398

Investigations into QWidget Qt::WA_WState_{Visible,Hidden,ExplicitShowHide} behavior

    XMLWordPrintable

Details

    • Epic
    • Resolution: Unresolved
    • P2: Important
    • None
    • None
    • None
    • Qt::WA_WState_{Visible,Hidden,ExplicitShowHide}
    • All
    • 4634fbf34 (dev), f57539d4f (6.7), 5ba0982b2 (dev), b393b26c7 (dev), f85c98898 (dev), 0b76a0b32 (6.7), 64f6664f5 (6.7), c8a543e3e (6.7), 1308f7e0b (dev), 2332a9c67 (6.7)

    Description

      Summary

      This is the current understanding. This may change.

      Qt::WA_WState_Hidden (as reflected by QWidget::isHidden()) is not just another way to express !Qt::WA_WState_Visible (as refelected by !QWidget::isVisible()). This is explicitly documented by QWidget::isHidden().

      The WA_WState_Visible attribute marks whether a widget is currently visible, while the Qt::WA_WState_Hidden attribute determines if the widget should automatically become visible as a result of its parent becoming visible.

      Top level widgets, or widgets created as children of already visible windows, will automatically have Qt::WA_WState_Hidden, and will need an explicit show() to be visible.

      Calling hide() or setVisible(false) on an existing widget will also add  Qt::WA_WState_Hidden,, making sure that the widget doesn't show at a later point without explicit action by the developer.

      Calling show() or setVisible(true) on a widget with  Qt::WA_WState_Hidden, will remove the attribute, making sure that the widget shows automatically at a later point if needed.

      As a result, a widget with  Qt::WA_WState_Hidden, can not also be visible, as any showing the widget would have cleared Qt::WA_WState_Hidden,.

      The inverse is possible however, as a widget without Qt::WA_WState_Hidden, can be !isVisible() because its parent has not been shown yet, but will be visible once the parent is shown.

      Possible combinations

      Attributes Visible Hidden ExplicitShowHide Situation
      No attributes       Initial state of child widget of non-visible parent
      Only hidden                                               Initial state of top level widget, or children of visible widgets
      Only visible           State after implicitly showing child
      Explicitly visible                                      State after explicitly showing widget
      Visible and hidden                                      Not possible/valid
      Explicitly hidden                                      State after explicitly hiding widget
      Only explicit                      Can happen. Unclear semantics
      All attributes                               Not possible/valid

      No attributes

      This state can happen when:

      • Creating a child widget of a non-visible parent

      The process is as follows:

      • The hidden attribute is initially set by QWidgetPrivate::init
      • As the widget is a child we end call setParent, which recurses into setParent_sys

      Only hidden

      This state can happen when:

      • Creating a top level widget
      • Creating a child widget of an already visible parent

      The process is as follows:

      We also have some places where we clear WState_ExplicitShowHide, putting us in this situation:

      • QSizeGrip::eventFilter for QEvent::WindowStateChange
        • After calling first calling QWidget::setVisible, so done to not overwrite WState_ExplicitShowHide
          • Should be replaced by call to QWidgetPrivate::setVisible
      • QStatusBar::hideOrShow()
        • After calling hide(), so done to not overwrite WState_ExplicitShowHide
          • Should be replaced by call to QWidgetPrivate::setVisible

      Only visible

      This state can happen when:

      • A child widget is shown implicitly by Qt

      The process is as follows:

      1 The fact that we call QWidget::setVisible and not QWidgetPrivate::setVisible means we currently apply ExplicitShowHide even to children we're showing ourselves, which seems like a bug.  Fixed by https://codereview.qt-project.org/c/qt/qtbase/+/536262

      We also have some places where we clear WState_ExplicitShowHide, putting us in this situation:

      • QStatusBarPrivate::tryToShowSizeGrip
        • Before and after triggering _q_showIfNotHidden for the internal size grip

      Explicitly visible

      This state can happen when:

      • A widget is shown "explicitly"
        • The definition of "explicit" is not well defined. Right now it includes
          • Calling QWidget::setVisible(true)
            • Possibly via QWidget::show()

      The process is as follows:

      Visible and hidden

      This state is not possible in practice, unless set explicitly via setAttribute.

      The QWidget::isHidden() documentation explicitly says that this can not happen.

      We should perhaps consider asserting somewhere that we don't end up in this situation.

      Explicitly hidden

      This state can happen when:

      • A widget is hidden "explicitly"
        • The definition of "explicit" is not well defined. Right now it includes
          • Calling QWidget::setVisible(false)

      The process is as follows:

      Only explicit 

      Under normal circumstances this should not happen.

      • We set WState_ExplicitShowHide from QWidget::setVisible()
        • Which in turn ends up with either WA_WState_Visible or WA_WState_Hidden set.
      • We set WState_ExplicitShowHide from QWidgetPrivate::setParent_sys()
        • We do so to restore the "explicitly hidden" state (Hidden+ExplicitShowHide) to what it was before we tweaked Visible/Hidden as part of the reparenting
          • We first unset both Visible and Hidden
          • Then apply Hidden if the widget is now a top level, or a has a visible parent, or was explicitly hidden
          • Then apply ExplicitShowHide if the widget was explicitly hidden
        • So the only way we can end up setting ExplicitShowHide is if the widget was already also Hidden, in which case the widget will also have Hidden when we're done with setParent_sys().
      • We set WState_ExplicitShowHide in the QRubberBand constructor
        • But follow it up with setVisible(false)
        • Making the final state "explicitly hidden"
        • We can remove the call to set WState_ExplicitShowHide, as QWidget::setVisible() will do that

      Some corner cases that end up here are:

      • Due to propagating WState_ExplicitShowHide to children on setVisible (likely a bug), we end up with children having WState_ExplicitShowHide after the parent is hidden
      • We set WState_ExplicitShowHide from QWidgetWindowPrivate::setVisible()
        • Triggered via QWindow::setVisible() 
        • We do so to restore the ExplicitShowHide state after calling QWidgetPrivate::setVisible()
          • Which we know will end up with either Visible or Hidden
        • But we then also restore WState_Hidden
          • Which means that if the original widget was explicitly visible, and then made invisible, we'll end up removing Visible via QWidgetPrivate::setVisible(), and then also removing Hidden, while keeping ExplicitShowHide.
        • Fixed in https://codereview.qt-project.org/c/qt/qtbase/+/539505
      • QAlphaWidget::run() and QRollEffect::run() set ExplicitShowHide and removes Hidden, saying "it is roughly equivalent to calling setVisible(true) without actually showing the widget".

      All attributes

      The same reasoning as for "Visible and hidden" should apply here. If a widget can't have both WState_Visible and WS_Hidden at the same time, it can't have those plus another attribute.

      2 If we end up QWidgetWindowPrivate::setVisible(true) for a already explicitly hidden widget, we'll end up setting WState_Visible via QWidgetPrivate::setVisible(), but then restore WState_Hidden and WState_ExplicitShowHide. This is a bug. Fixed in https://codereview.qt-project.org/c/qt/qtbase/+/539505

      During QWidget::destroy() we set WA_WState_Created to false, and then delete the QWidgetWindow. As a result we skip calling hide_helper() from QWidgetPrivate::setVisible(false), where we normally unset WA_WState_Visible, and the widget ends up isVisible() even after being destroyed. This is a bug. Fixed in https://codereview.qt-project.org/c/qt/qtbase/+/539506/2

      History

      Qt::WA_WState_ExplicitShowHide

      Introduced as CreateHidden

      commit fafd940011ba126eefd3f4d30c2492a5f9dcfacd
      Date:   Tue Jul 30 16:05:15 2002 +0100
      
          1. Added the possibility to hide menu and menubar items
             similar to widgets
          2. Make use of this feature in QAction
          3. Added a tricky way (via WState_CreatedHidden) to distinguish
             explicit user hide()s from implicit hides. This allows us to
             implement menu item semantics with toolbar widgets:
                        new QToolButton( toolBar );
                    => always shows the button through ChildEvent insert
      
                 (new QToolButton( toolBar ))->hide();
                    => does *not* show the button [ new! ]
      
          New functions:
          QMenuData::setItemVisible(bool), implemented for both
          menu bar and popup menu.
          QAction::setVisible(bool) ( QActionGroup::setVisible(bool) )

      Renamed in a721e2dd3bb2d5a953a09f8adfb47a95b548b2bb

      • WState_ForceHide ➡ WState_Hidden
      • WState_CreatedHiddenWState_ExplicitShowHide

      Unanswered questions

      What are the practical implications of the three different non-visible states?

       

      Attributes Visible Hidden ExplicitShowHide Situation
      No attributes       Not visible, but also not hidden
      Only hidden                                               Hidden, but not explicitly
      Explicitly hidden                                      Explicitly hidden

      We treat these states differently inside Qt, but exactly how needs some more diggging. 

      What does it mean to be "explicitly" shown/hidden?

      What kind of actions will/should result in this state?

      • QWidget
        • setVisible ( today, also for children)
        • show ( today, also for children)
        • hide ( today, also for children as a result of the show)
        • close ( today)
      • QWindow
        • setVisible
        • show
        • hide 
        • close 
      • User action
        • Clicking the close button of a window
        • Minimizing a window
      • System actions
        • System shutdown, closing all apps and their windows

      The answer to these questions are likely informed by the answers to what the practical implication of being "explicit" is.

      Should ExplicitShowHide persist

      • Is the action of explicitly showing a widget something that should persist into the widget being "explicitly hidden" once hidden? And vice versa?
      • Should Qt ever reset/clear this attribute when it internally does show/hide that would not normally have added this attribute, or should it make sure the attribute state is kept as is?
      • Should the attribute persist when saving and restoring the widget state to/from disk?

       

      Attachments

        For Gerrit Dashboard: QTBUG-121398
        # Subject Branch Project Status CR V

        Activity

          People

            vestbo Tor Arne Vestbø
            vestbo Tor Arne Vestbø
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated: