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 }