1 // Written in the D programming language. 2 3 /** 4 This module contains popup widgets implementation. 5 6 Popups appear above other widgets inside window. 7 8 Useful for popup menus, notification popups, etc. 9 10 Synopsis: 11 12 ---- 13 import dlangui.widgets.popup; 14 15 ---- 16 17 Copyright: Vadim Lopatin, 2014 18 License: Boost License 1.0 19 Authors: Vadim Lopatin, coolreader.org@gmail.com 20 */ 21 module dlangui.widgets.popup; 22 23 import dlangui.widgets.widget; 24 import dlangui.widgets.layouts; 25 import dlangui.core.signals; 26 import dlangui.platforms.common.platform; 27 28 /// popup alignment option flags 29 enum PopupAlign : uint { 30 /// center popup around anchor widget center 31 Center = 1, 32 /// place popup below anchor widget close to lower bound 33 Below = 2, 34 /// place popup below anchor widget close to lower bound 35 Above = 16, 36 /// place popup below anchor widget close to right bound (when no space enough, align near left bound) 37 Right = 4, 38 /// align to specified point 39 Point = 8, 40 /// if popup content size is less than anchor's size, increase it to anchor size 41 FitAnchorSize = 16, 42 } 43 44 struct PopupAnchor { 45 Widget widget; 46 int x; 47 int y; 48 uint alignment = PopupAlign.Center; 49 } 50 51 /// popup behavior flags - for PopupWidget.flags property 52 enum PopupFlags : uint { 53 /// close popup when mouse button clicked outside of its bounds 54 CloseOnClickOutside = 1, 55 /// modal popup - keypresses and mouse events can be routed to this popup only 56 Modal = 2, 57 /// close popup when mouse is moved outside this popup 58 CloseOnMouseMoveOutside = 4, 59 } 60 61 /** interface - slot for onPopupCloseListener */ 62 interface OnPopupCloseHandler { 63 void onPopupClosed(PopupWidget source); 64 } 65 66 /// popup widget container 67 class PopupWidget : LinearLayout { 68 protected PopupAnchor _anchor; 69 protected bool _modal; 70 71 protected uint _flags; 72 /** popup close signal */ 73 Signal!OnPopupCloseHandler popupClosed; 74 //protected void delegate(PopupWidget popup) _onPopupCloseListener; 75 /// popup close listener (called right before closing) 76 //@property void delegate(PopupWidget popup) onPopupCloseListener() { return _onPopupCloseListener; } 77 /// set popup close listener (to call right before closing) 78 //@property PopupWidget onPopupCloseListener(void delegate(PopupWidget popup) listener) { _onPopupCloseListener = listener; return this; } 79 80 /// returns popup behavior flags (combination of PopupFlags) 81 @property uint flags() { return _flags; } 82 /// set popup behavior flags (combination of PopupFlags) 83 @property PopupWidget flags(uint flags) { _flags = flags; return this; } 84 85 /// access to popup anchor 86 @property ref PopupAnchor anchor() { return _anchor; } 87 /// returns true if popup is modal 88 bool modal() { return _modal; } 89 /// set modality flag 90 PopupWidget modal(bool modal) { _modal = modal; return this; } 91 92 /// Measure widget according to desired width and height constraints. (Step 1 of two phase layout). 93 override void measure(int parentWidth, int parentHeight) { 94 super.measure(parentWidth, parentHeight); 95 } 96 /// close and destroy popup 97 void close() { 98 window.removePopup(this); 99 } 100 101 /// just call on close listener 102 void onClose() { 103 if (popupClosed.assigned) 104 popupClosed(this); 105 } 106 107 /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout). 108 override void layout(Rect rc) { 109 if (visibility == Visibility.Gone) { 110 return; 111 } 112 int w = measuredWidth; 113 int h = measuredHeight; 114 if (w > rc.width) 115 w = rc.width; 116 if (h > rc.height) 117 h = rc.height; 118 119 Rect anchorrc; 120 if (anchor.widget !is null) 121 anchorrc = anchor.widget.pos; 122 else 123 anchorrc = rc; 124 125 Rect r; 126 Point anchorPt; 127 128 if (anchor.alignment & PopupAlign.Point) { 129 r.left = anchor.x; 130 r.top = anchor.y; 131 if (anchor.alignment & PopupAlign.Center) { 132 // center around center of anchor widget 133 r.left -= w / 2; 134 r.top -= h / 2; 135 } else if (anchor.alignment & PopupAlign.Below) { 136 } else if (anchor.alignment & PopupAlign.Above) { 137 r.top -= h; 138 } else if (anchor.alignment & PopupAlign.Right) { 139 } 140 } else { 141 if (anchor.alignment & PopupAlign.Center) { 142 // center around center of anchor widget 143 r.left = anchorrc.middlex - w / 2; 144 r.top = anchorrc.middley - h / 2; 145 } else if (anchor.alignment & PopupAlign.Below) { 146 r.left = anchorrc.left; 147 r.top = anchorrc.bottom; 148 } else if (anchor.alignment & PopupAlign.Above) { 149 r.left = anchorrc.left; 150 r.top = anchorrc.top - h; 151 } else if (anchor.alignment & PopupAlign.Right) { 152 r.left = anchorrc.right; 153 r.top = anchorrc.top; 154 } 155 if (anchor.alignment & PopupAlign.FitAnchorSize) 156 if (w < anchorrc.width) 157 w = anchorrc.width; 158 } 159 r.right = r.left + w; 160 r.bottom = r.top + h; 161 r.moveToFit(rc); 162 super.layout(r); 163 } 164 165 this(Widget content, Window window) { 166 super("POPUP"); 167 _window = window; 168 //styleId = "POPUP_MENU"; 169 addChild(content); 170 } 171 172 /// called for mouse activity outside shown popup bounds 173 bool onMouseEventOutside(MouseEvent event) { 174 if (visibility != Visibility.Visible) 175 return false; 176 if (_flags & PopupFlags.CloseOnClickOutside) { 177 if (event.action == MouseAction.ButtonDown) { 178 // clicked outside - close popup 179 close(); 180 return false; 181 } 182 } 183 if (_flags & PopupFlags.CloseOnMouseMoveOutside) { 184 if (event.action == MouseAction.Move || event.action == MouseAction.Wheel) { 185 int threshold = 3; 186 if (event.x < _pos.left - threshold || event.x > _pos.right + threshold || event.y < _pos.top - threshold || event.y > _pos.bottom + threshold) { 187 Log.d("Closing popup due to PopupFlags.CloseOnMouseMoveOutside flag"); 188 close(); 189 return false; 190 } 191 } 192 } 193 return false; 194 } 195 }