1 module nanogui.formhelper; 2 /* 3 NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>. 4 The widget drawing code is based on the NanoVG demo application 5 by Mikko Mononen. 6 7 All rights reserved. Use of this source code is governed by a 8 BSD-style license that can be found in the LICENSE.txt file. 9 */ 10 /** 11 * \file nanogui/formhelper.h 12 * 13 * \brief Helper class to construct forms for editing a set of variables of 14 * various types. 15 */ 16 import std.container : Array; 17 18 import nanogui.common; 19 import nanogui.screen : Screen; 20 import nanogui.label : Label; 21 import nanogui.button : Button; 22 import nanogui.checkbox : CheckBox; 23 import nanogui.textbox : TextBox, FloatBox, IntBox; 24 import nanogui.combobox : ComboBox; 25 import nanogui.layout : AdvancedGridLayout; 26 import nanogui.window : Window; 27 import nanogui.widget : Widget; 28 29 /** 30 * \class FormHelper formhelper.h nanogui/formhelper.h 31 * 32 * \brief Convenience class to create simple AntTweakBar-style layouts that 33 * expose variables of various types using NanoGUI widgets 34 * 35 */ 36 class FormHelper { 37 public: 38 /// Create a helper class to construct NanoGUI widgets on the given screen 39 this(Screen screen) { mScreen = screen; } 40 41 /// Add a new top-level window 42 Window addWindow(const Vector2i pos, 43 const string title = "Untitled") 44 { 45 assert(mScreen); 46 mWindow = new Window(mScreen, title); 47 mLayout = new AdvancedGridLayout([10, 0, 10, 0], []); 48 mLayout.margin(10); 49 mLayout.setColStretch(2, 1); 50 mWindow.position = pos; 51 mWindow.layout = mLayout; 52 mWindow.visible = true; 53 return mWindow; 54 } 55 56 /// Add a new group that may contain several sub-widgets 57 Label addGroup(string caption) 58 { 59 Label label = new Label(mWindow, caption, mGroupFontName, mGroupFontSize); 60 if (mLayout.rowCount() > 0) 61 mLayout.appendRow(mPreGroupSpacing); /* Spacing */ 62 mLayout.appendRow(0); 63 mLayout.setAnchor(label, AdvancedGridLayout.Anchor(0, mLayout.rowCount()-1, 4, 1)); 64 mLayout.appendRow(mPostGroupSpacing); 65 return label; 66 } 67 68 auto addVariable(Type)(string label, void delegate(Type) setter, 69 const Type delegate() getter, bool editable = true) 70 { 71 Label labelW = new Label(mWindow, label, mLabelFontName, mLabelFontSize); 72 auto widget = new FormWidget!Type(mWindow); 73 void delegate() refresh; 74 (widget, getter){ refresh = { 75 Type value = getter(), current = widget.value; 76 if (value != current) widget.value = value; 77 }; 78 }(widget, getter); 79 80 refresh(); 81 82 widget.callback = setter; 83 widget.editable = editable; 84 widget.fontSize = mWidgetFontSize; 85 Vector2i fs = widget.fixedSize(); 86 widget.fixedSize =Vector2i(fs.x != 0 ? fs.x : mFixedSize.x, 87 fs.y != 0 ? fs.y : mFixedSize.y); 88 mRefreshCallbacks.insertBack(refresh); 89 if (mLayout.rowCount() > 0) 90 mLayout.appendRow(mVariableSpacing); 91 mLayout.appendRow(0); 92 mLayout.setAnchor(labelW, AdvancedGridLayout.Anchor(1, mLayout.rowCount()-1)); 93 mLayout.setAnchor(widget, AdvancedGridLayout.Anchor(3, mLayout.rowCount()-1)); 94 return cast(FormWidget!Type) widget; 95 } 96 97 auto addVariable(Type)(string label, ref Type value, bool editable = true) 98 { 99 return addVariable!Type(label, (v) { value = v; }, 100 () { return value; }, editable); 101 } 102 103 /// Add a button with a custom callback 104 Button addButton(const string label, void delegate() cb) 105 { 106 Button button = new Button(mWindow, label); 107 button.callback = cb; 108 button.fixedHeight = 25; 109 if (mLayout.rowCount() > 0) 110 mLayout.appendRow(mVariableSpacing); 111 mLayout.appendRow(0); 112 mLayout.setAnchor(button, AdvancedGridLayout.Anchor(1, mLayout.rowCount()-1, 3, 1)); 113 return button; 114 } 115 116 /// Add an arbitrary (optionally labeled) widget to the layout 117 void addWidget(const string label, Widget widget) 118 { 119 mLayout.appendRow(0); 120 if (label == "") { 121 mLayout.setAnchor(widget, AdvancedGridLayout.Anchor(1, mLayout.rowCount()-1, 3, 1)); 122 } else { 123 Label labelW = new Label(mWindow, label, mLabelFontName, mLabelFontSize); 124 mLayout.setAnchor(labelW, AdvancedGridLayout.Anchor(1, mLayout.rowCount()-1)); 125 mLayout.setAnchor(widget, AdvancedGridLayout.Anchor(3, mLayout.rowCount()-1)); 126 } 127 } 128 129 /// Cause all widgets to re-synchronize with the underlying variable state 130 void refresh() 131 { 132 foreach (const callback; mRefreshCallbacks) { 133 callback(); 134 } 135 } 136 137 /// Access the currently active \ref Window instance 138 Window window() { return mWindow; } 139 140 /// Set the active \ref Window instance. 141 void setWindow(Window window) 142 { 143 mWindow = window; 144 mLayout = cast(AdvancedGridLayout)window.layout; 145 if (mLayout is null) 146 throw new Exception( 147 "Internal error: window has an incompatible layout!"); 148 } 149 150 /// Specify a fixed size for newly added widgets. 151 void setFixedSize(ref const Vector2i fw) { mFixedSize = fw; } 152 153 /// The current fixed size being used for newly added widgets. 154 Vector2i fixedSize() { return mFixedSize; } 155 156 /// The font name being used for group headers. 157 string groupFontName() const { return mGroupFontName; } 158 159 /// Sets the font name to be used for group headers. 160 void setGroupFontName(const string name) { mGroupFontName = name; } 161 162 /// The font name being used for labels. 163 string labelFontName() const { return mLabelFontName; } 164 165 /// Sets the font name being used for labels. 166 void setLabelFontName(const string name) { mLabelFontName = name; } 167 168 /// The size of the font being used for group headers. 169 int groupFontSize() const { return mGroupFontSize; } 170 171 /// Sets the size of the font being used for group headers. 172 void setGroupFontSize(int value) { mGroupFontSize = value; } 173 174 /// The size of the font being used for labels. 175 int labelFontSize() const { return mLabelFontSize; } 176 177 /// Sets the size of the font being used for labels. 178 void setLabelFontSize(int value) { mLabelFontSize = value; } 179 180 /// The size of the font being used for non-group / non-label widgets. 181 int widgetFontSize() const { return mWidgetFontSize; } 182 183 /// Sets the size of the font being used for non-group / non-label widgets. 184 void setWidgetFontSize(int value) { mWidgetFontSize = value; } 185 186 protected: 187 /// A reference to the \ref nanogui::Screen this FormHelper is assisting. 188 Screen mScreen; 189 190 /// A reference to the \ref nanogui::Window this FormHelper is controlling. 191 Window mWindow; 192 193 /// A reference to the \ref nanogui::AdvancedGridLayout this FormHelper is using. 194 AdvancedGridLayout mLayout; 195 196 /// The callbacks associated with all widgets this FormHelper is managing. 197 Array!(void delegate()) mRefreshCallbacks; 198 199 /// The group header font name. 200 string mGroupFontName = "sans-bold"; 201 202 /// The label font name. 203 string mLabelFontName = "sans"; 204 205 /// The fixed size for newly added widgets. 206 Vector2i mFixedSize = Vector2i(0, 20); 207 208 /// The font size for group headers. 209 int mGroupFontSize = 20; 210 211 /// The font size for labels. 212 int mLabelFontSize = 16; 213 214 /// The font size for non-group / non-label widgets. 215 int mWidgetFontSize = 16; 216 217 /// The spacing used **before** new groups. 218 int mPreGroupSpacing = 15; 219 220 /// The spacing used **after** each group. 221 int mPostGroupSpacing = 5; 222 223 /// The spacing between all other widgets. 224 int mVariableSpacing = 5; 225 226 public: 227 // EIGEN_MAKE_ALIGNED_OPERATOR_NEW 228 }; 229 230 231 /** 232 * \class FormWidget formhelper.h nanogui/formhelper.h 233 * 234 * \brief A template wrapper class for assisting in the creation of various form widgets. 235 */ 236 import std.traits : isBoolean, isFloatingPoint, isSomeString, isIntegral; 237 238 class FormWidget(T) : CheckBox if(isBoolean!T) 239 { 240 this(Widget p) { super(p, "", null); fixedWidth = 20; } 241 242 alias value = checked; 243 alias editable = enabled; 244 } 245 246 class FormWidget(T) : IntBox!T if (isIntegral!T) 247 { 248 this(Widget p) { super(p); alignment = TextBox.Alignment.Right; } 249 } 250 251 class FormWidget(T) : FloatBox!T if (isFloatingPoint!T) 252 { 253 this(Widget p) { super(p); alignment = TextBox.Alignment.Right; } 254 } 255 256 class FormWidget(T) : TextBox if (isSomeString!T) 257 { 258 this(Widget p) { super(p); alignment = TextBox.Alignment.Left; } 259 260 void callback(void delegate(string) cb) { 261 super.callback = (string str) { cb(str); return true; }; 262 } 263 } 264 265 /+ 266 template <typename T> class FormWidget<T, typename std::is_enum<T>::type> : public ComboBox { 267 public: 268 /// Creates a new FormWidget with underlying type ComboBox. 269 FormWidget(Widget *p) : ComboBox(p) { } 270 271 /// Pass-through function for \ref nanogui::ComboBox::selectedIndex. 272 T value() const { return (T) selectedIndex(); } 273 274 /// Pass-through function for \ref nanogui::ComboBox::setSelectedIndex. 275 void setValue(T value) { setSelectedIndex((int) value); mSelectedIndex = (int) value; } 276 277 /// Pass-through function for \ref nanogui::ComboBox::setCallback. 278 void setCallback(const std::function<void(const T &)> &cb) { 279 ComboBox::setCallback([cb](int v) { cb((T) v); }); 280 } 281 282 /// Pass-through function for \ref nanogui::Widget::setEnabled. 283 void setEditable(bool e) { setEnabled(e); } 284 285 public: 286 EIGEN_MAKE_ALIGNED_OPERATOR_NEW 287 }; 288 +/