1 // Written in the D programming language. 2 3 /** 4 This module contains common Dialog implementation. 5 6 7 Use to create custom dialogs. 8 9 Synopsis: 10 11 ---- 12 import dlangui.dialogs.dialog; 13 14 ---- 15 16 Copyright: Vadim Lopatin, 2014 17 License: Boost License 1.0 18 Authors: Vadim Lopatin, coolreader.org@gmail.com 19 */ 20 module dlangui.dialogs.dialog; 21 22 import dlangui.core.i18n; 23 import dlangui.core.signals; 24 import dlangui.core.stdaction; 25 import dlangui.widgets.layouts; 26 import dlangui.widgets.controls; 27 import dlangui.widgets.winframe; 28 import dlangui.widgets.popup; 29 import dlangui.platforms.common.platform; 30 31 import std.conv; 32 33 /// dialog flag bits 34 enum DialogFlag : uint { 35 /// dialog is modal 36 Modal = 1, 37 /// dialog can be resized 38 Resizable = 2, 39 /// dialog is show in popup widget inside current window instead of separate window 40 Popup = 4, 41 } 42 43 /// slot to pass dialog result 44 interface DialogResultHandler { 45 public void onDialogResult(Dialog dlg, const Action result); 46 } 47 48 /// base for all dialogs 49 class Dialog : VerticalLayout { 50 protected Window _window; 51 protected Window _parentWindow; 52 protected PopupWidget _popup; 53 protected UIString _caption; 54 protected uint _flags; 55 protected string _icon; 56 protected int _initialWidth; 57 protected int _initialHeight; 58 59 Signal!DialogResultHandler dialogResult; 60 61 this(UIString caption, Window parentWindow = null, uint flags = DialogFlag.Modal, int initialWidth = 0, int initialHeight = 0) { 62 super("dialog-main-widget"); 63 _initialWidth = initialWidth; 64 _initialHeight = initialHeight; 65 _caption = caption; 66 _parentWindow = parentWindow; 67 _flags = flags; 68 _icon = "dlangui-logo1"; 69 } 70 71 /** 72 Measure widget according to desired width and height constraints. (Step 1 of two phase layout). 73 */ 74 override void measure(int parentWidth, int parentHeight) { 75 super.measure(parentWidth, parentHeight); 76 if ((_flags & DialogFlag.Resizable) && (_flags & DialogFlag.Popup)) { 77 Point sz = Point(_parentWindow.width * 4 / 5, _parentWindow.height * 4 / 5); 78 measuredContent(parentWidth, parentHeight, sz.x, sz.y); 79 } 80 } 81 82 /// get icon resource id 83 @property string windowIcon() { 84 return _icon; 85 } 86 87 /// set icon resource id 88 @property Dialog windowIcon(string iconResourceId) { 89 _icon = iconResourceId; 90 static if (BACKEND_GUI) { 91 if (_window && _icon) 92 _window.windowIcon = drawableCache.getImage(_icon); 93 } 94 return this; 95 } 96 97 @property UIString windowCaption() { 98 return _caption; 99 } 100 101 /// set window caption 102 @property Dialog windowCaption(dstring caption) { 103 _caption = caption; 104 if (_window) 105 _window.windowCaption = caption; 106 return this; 107 } 108 109 /// get window caption 110 @property Dialog windowCaption(UIString caption) { 111 _caption = caption; 112 if (_window) 113 _window.windowCaption = caption; 114 return this; 115 } 116 117 protected const(Action) [] _buttonActions; 118 119 protected ImageTextButton _defaultButton; 120 protected ImageTextButton _cancelButton; 121 /// create panel with buttons based on list of actions 122 Widget createButtonsPanel(const(Action) [] actions, int defaultActionIndex, int splitBeforeIndex) { 123 _buttonActions = actions; 124 LinearLayout res = new HorizontalLayout("buttons"); 125 res.layoutWidth(FILL_PARENT); 126 res.layoutWeight = 0; 127 for (int i = 0; i < actions.length; i++) { 128 if (splitBeforeIndex == i) 129 res.addChild(new HSpacer()); 130 const Action a = actions[i]; 131 string id = "btn" ~ to!string(a.id); 132 ImageTextButton btn = new ImageTextButton(id, a.iconId, a.label); 133 if (defaultActionIndex == i) { 134 btn.setState(State.Default); 135 _defaultButton = btn; 136 } 137 if (a.id == StandardAction.Cancel || a.id == StandardAction.No) 138 _cancelButton = btn; 139 btn.action = a.clone(); 140 res.addChild(btn); 141 } 142 return res; 143 } 144 145 /// map key to action 146 override Action findKeyAction(uint keyCode, uint flags) { 147 foreach(a; _buttonActions) { 148 if (a.checkAccelerator(keyCode, flags)) 149 return a.clone; 150 } 151 return super.findKeyAction(keyCode, flags); 152 } 153 154 /// Custom handling of actions 155 override bool handleAction(const Action action) { 156 foreach(const Action a; _buttonActions) 157 if (a.id == action.id) { 158 close(action); 159 return true; 160 } 161 return false; 162 } 163 164 /// override to implement creation of dialog controls 165 void initialize() { 166 } 167 168 /** Notify about dialog result, and then close dialog. 169 170 If onDialogResult listener is assigned, pass action to it. 171 172 If no onDialogResult listener, pass to owner window. 173 174 If action is null, no result dispatching will occur. 175 */ 176 void close(const Action action) { 177 if (action) { 178 if (dialogResult.assigned) 179 dialogResult(this, action); 180 else if (_parentWindow && !_popup) 181 _parentWindow.dispatchAction(action); 182 } 183 if (_popup) 184 _parentWindow.removePopup(_popup); 185 else 186 window.close(); 187 } 188 189 /// shows dialog 190 void show() { 191 initialize(); 192 uint wflags = 0; 193 if (_flags & DialogFlag.Modal) 194 wflags |= WindowFlag.Modal; 195 if (_flags & DialogFlag.Resizable) { 196 wflags |= WindowFlag.Resizable; 197 layoutWidth = FILL_PARENT; 198 layoutHeight = FILL_PARENT; 199 } 200 if (_flags & DialogFlag.Popup) { 201 DialogFrame _frame = new DialogFrame(this, _cancelButton !is null); 202 if (_cancelButton) { 203 _frame.closeButtonClick = delegate(Widget w) { 204 close(_cancelButton.action); 205 return true; 206 }; 207 } 208 _popup = _parentWindow.showPopup(_frame); 209 _popup.flags(PopupFlags.Modal); 210 } else { 211 _window = Platform.instance.createWindow(_caption, _parentWindow, wflags, _initialWidth, _initialHeight); 212 static if (BACKEND_GUI) { 213 if (_window && _icon) 214 _window.windowIcon = drawableCache.getImage(_icon); 215 } 216 _window.backgroundColor = currentTheme.customColor("dialog_background"); 217 _window.mainWidget = this; 218 _window.show(); 219 } 220 onShow(); 221 } 222 223 /// called after window with dialog is shown 224 void onShow() { 225 // override to do something useful 226 if (_defaultButton) 227 _defaultButton.setFocus(); 228 } 229 } 230 231 /// frame with caption for dialog 232 class DialogFrame : WindowFrame { 233 protected Dialog _dialog; 234 this(Dialog dialog, bool enableCloseButton) { 235 super(dialog.id ~ "_frame", enableCloseButton); 236 styleId = STYLE_FLOATING_WINDOW; 237 _dialog = dialog; 238 _caption.text = _dialog.windowCaption.value; 239 bodyWidget = _dialog; 240 } 241 }