1 /** @file widgets/dialogwidget.h Popup dialog. 2 * 3 * @authors Copyright (c) 2013-2017 Jaakko Keränen <jaakko.keranen@iki.fi> 4 * 5 * @par License 6 * LGPL: http://www.gnu.org/licenses/lgpl.html 7 * 8 * <small>This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU Lesser General Public License as published by 10 * the Free Software Foundation; either version 3 of the License, or (at your 11 * option) any later version. This program is distributed in the hope that it 12 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 14 * General Public License for more details. You should have received a copy of 15 * the GNU Lesser General Public License along with this program; if not, see: 16 * http://www.gnu.org/licenses</small> 17 */ 18 19 #ifndef LIBAPPFW_DIALOGWIDGET_H 20 #define LIBAPPFW_DIALOGWIDGET_H 21 22 #include "popupwidget.h" 23 #include "scrollareawidget.h" 24 #include "menuwidget.h" 25 #include "popupbuttonwidget.h" 26 27 namespace de { 28 29 class GuiRootWidget; 30 class DialogContentStylist; 31 32 /** 33 * Popup dialog. 34 * 35 * The content area of a dialog is scrollable. A menu with buttons is placed in 36 * the bottom of the dialog, for the actions available to the user. 37 * 38 * The contents of the dialog should be placed under the scroll area returned 39 * by DialogWidget::area() and positioned in relation to its content rule. 40 * When the dialog is set up, one must define the size of the content scroll 41 * area (width and height rules). 42 * 43 * Note that when a widget is added to the content area, the dialog 44 * automatically applies certain common style parameters (margins, backgrounds, 45 * etc.). 46 * 47 * @par Widget Structure 48 * 49 * Dialogs are composed of several child widgets: 50 * <pre> 51 * DialogWidget (PopupWidget) 52 * +- container (GuiWidget; the popup content widget) 53 * +- heading (LabelWidget; optional) 54 * +- area (ScrollAreaWidget; contains actual dialog widgets) 55 * +- buttons (MenuWidget) 56 * +- extra (MenuWidget; might be empty) 57 * </pre> 58 * 59 * Scrolling is set up so that the dialog height doesn't surpass the view 60 * rectangle's height. Contents of the "area" widget scroll while the other 61 * elements remain static in relation to the container. 62 * 63 * The dialog can optionally have two scrollable content areas side-by-side. 64 * 65 * @ingroup guiWidgets 66 */ 67 class LIBAPPFW_PUBLIC DialogWidget : public PopupWidget 68 { 69 Q_OBJECT 70 71 public: 72 /** 73 * Modality of the dialog. By default, dialogs are modal, meaning that 74 * while they are open, no events can get past the dialog. 75 */ 76 enum Modality { 77 Modal, 78 NonModal 79 }; 80 81 enum Flag { 82 DefaultFlags = 0, 83 WithHeading = 0x1 ///< Dialog has a heading above the content area. 84 }; 85 Q_DECLARE_FLAGS(Flags, Flag) 86 87 enum RoleFlag { 88 None = 0, 89 Default = 0x1, ///< Pressing Space or Enter will activate this. 90 Accept = 0x2, 91 Reject = 0x4, 92 Yes = 0x8, 93 No = 0x10, 94 Action = 0x20, 95 Popup = 0x40, ///< Uses a PopupButtonWidget. 96 97 ActionPopup = Action | Popup, 98 99 IdMask = 0xff0000, 100 Id1 = 0x010000, 101 Id2 = 0x020000, 102 Id3 = 0x030000, 103 Id4 = 0x040000 104 }; Q_DECLARE_FLAGS(RoleFlags,RoleFlag)105 Q_DECLARE_FLAGS(RoleFlags, RoleFlag) 106 107 /** 108 * All buttons in a dialog must be ButtonItem instances or instances of 109 * derived classes. 110 * 111 * The DialogButtonItem typedef is provided for convenience. 112 */ 113 class LIBAPPFW_PUBLIC ButtonItem : public ui::ActionItem 114 { 115 public: 116 /** 117 * Button with the role's default label and action. 118 * @param flags Role flags for the button. 119 * @param label Label for the button. If empty, the default label will be used. 120 */ 121 ButtonItem(RoleFlags flags, String const &label = ""); 122 123 ButtonItem(RoleFlags flags, Image const &image); 124 125 /** 126 * Button with custom action. 127 * @param flags Role flags for the button. 128 * @param label Label for the button. If empty, the default label will be used. 129 * @param action Action for the button. 130 */ 131 ButtonItem(RoleFlags flags, String const &label, RefArg<de::Action> action); 132 133 ButtonItem(RoleFlags flags, Image const &image, RefArg<de::Action> action); 134 135 ButtonItem(RoleFlags flags, Image const &image, String const &label, 136 RefArg<de::Action> action = RefArg<de::Action>()); 137 138 RoleFlags role() const { return _role; } 139 140 static Semantics itemSemantics(RoleFlags flags); 141 142 private: 143 RoleFlags _role; 144 }; 145 146 /// Asked for a label that does not exist in the dialog. @ingroup errors 147 DENG2_ERROR(UndefinedLabel); 148 149 public: 150 DialogWidget(String const &name = String(), Flags const &flags = DefaultFlags); 151 152 Modality modality() const; 153 154 /** 155 * If the dialog was created using the WithHeading flag, this will return the 156 * label used for the dialog heading. 157 */ 158 LabelWidget &heading(); 159 160 ScrollAreaWidget &area(); 161 162 /** 163 * Returns the left content area. The first time this is called, the dialog 164 * layout is configured for two column areas. 165 */ 166 ScrollAreaWidget &leftArea(); 167 168 /** 169 * Returns the right content area. The first time this is called, the dialog 170 * layout is configured for two column areas. 171 */ 172 ScrollAreaWidget &rightArea(); 173 174 /** 175 * Sets the rule for the minimum width of the dialog. The default is that the 176 * dialog is at least as wide as the content area, or all the button widths 177 * summed together. 178 * 179 * @param minWidth Custom minimum width for the dialog. 180 */ 181 void setMinimumContentWidth(Rule const &minWidth); 182 183 void setMaximumContentHeight(Rule const &maxHeight); 184 185 MenuWidget &buttonsMenu(); 186 187 /** 188 * Additional buttons of the dialog, laid out opposite to the normal dialog 189 * buttons. These are used for functionality related to the dialog's content, 190 * but they don't accept or reject the dialog. For instance, shortcut for 191 * settings, showing what's new in an update, etc. 192 * 193 * @return Widget for dialog's extra buttons. 194 */ 195 MenuWidget &extraButtonsMenu(); 196 197 ui::Data &buttons(); 198 199 ButtonWidget &buttonWidget(String const &label) const; 200 PopupButtonWidget &popupButtonWidget(String const &label) const; 201 202 ButtonWidget *buttonWidget(int roleId) const; 203 PopupButtonWidget *popupButtonWidget(int roleId) const; 204 205 QList<ButtonWidget *> buttonWidgets() const; 206 207 /** 208 * Sets the action that will be triggered if the dialog is accepted. The action 209 * will be triggered after the dialog has started closing (called from 210 * DialogWidget::finish()). 211 * 212 * @param action Action to trigger after the dialog has been accepted. 213 */ 214 void setAcceptanceAction(RefArg<de::Action> action); 215 216 de::Action *acceptanceAction() const; 217 218 /** 219 * Shows the dialog and blocks execution until the dialog is closed -- 220 * another event loop is started for event processing. Call either accept() 221 * or reject() to dismiss the dialog. 222 * 223 * The dialog must not be part of the widget tree when this is called. 224 * 225 * @param root Root where to execute the dialog. 226 * 227 * @return Result code. 228 */ 229 int exec(GuiRootWidget &root); 230 231 /** 232 * Opens the dialog as non-modal. The dialog must already be added to the 233 * widget tree. Use accept() or reject() to close the dialog. 234 */ 235 void open() override; 236 237 void open(Modality modality); 238 239 ui::ActionItem *defaultActionItem(); 240 241 // Events. 242 void offerFocus() override; 243 void update() override; 244 bool handleEvent(Event const &event) override; 245 246 public slots: 247 void accept(int result = 1); 248 void reject(int result = 0); 249 250 signals: 251 void accepted(int result); 252 void rejected(int result); 253 254 protected: 255 void preparePanelForOpening() override; 256 257 /** 258 * Derived classes can override this to do additional tasks before 259 * execution of the dialog begins. DialogWidget::prepare() must be called 260 * from the overridding methods. The focused widget is reset in the method. 261 */ 262 virtual void prepare(); 263 264 /** 265 * Handles any tasks needed when the dialog is closing. 266 * DialogWidget::finish() must be called from overridding methods. 267 * 268 * @param result Result code. 269 */ 270 virtual void finish(int result); 271 272 private: 273 DENG2_PRIVATE(d) 274 }; 275 276 typedef DialogWidget::ButtonItem DialogButtonItem; 277 278 Q_DECLARE_OPERATORS_FOR_FLAGS(DialogWidget::Flags) 279 Q_DECLARE_OPERATORS_FOR_FLAGS(DialogWidget::RoleFlags) 280 281 } // namespace de 282 283 #endif // LIBAPPFW_DIALOGWIDGET_H 284