1 /// 2 module nanogui.popup; 3 /* 4 nanogui/popup.h -- Simple popup widget which is attached to another given 5 window (can be nested) 6 7 NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>. 8 The widget drawing code is based on the NanoVG demo application 9 by Mikko Mononen. 10 11 All rights reserved. Use of this source code is governed by a 12 BSD-style license that can be found in the LICENSE.txt file. 13 */ 14 15 import nanogui.window : Window; 16 import nanogui.widget : Widget, NVGContext; 17 import nanogui.common : Vector2i; 18 19 /** 20 * Popup window for combo boxes, popup buttons, nested dialogs etc. 21 * 22 * Usually the Popup instance is constructed by another widget (e.g. `PopupButton`) 23 * and does not need to be created by hand. 24 */ 25 class Popup : Window 26 { 27 public: 28 enum Side { Left = 0, Right } 29 30 /// Create a new popup parented to a screen (first argument) and a parent window 31 this(Widget parent, Window parentWindow) 32 { 33 super(parent, ""); 34 mParentWindow = parentWindow; 35 mAnchorHeight = 30; 36 mAnchorPos = Vector2i(0, 0); 37 mSide = Side.Right; 38 } 39 40 /// Return the anchor position in the parent window; the placement of the popup is relative to it 41 final void anchorPos(Vector2i anchorPos) { mAnchorPos = anchorPos; } 42 /// Set the anchor position in the parent window; the placement of the popup is relative to it 43 final Vector2i anchorPos() const { return mAnchorPos; } 44 45 /// Set the anchor height; this determines the vertical shift relative to the anchor position 46 final void anchorHeight(int anchorHeight) { mAnchorHeight = anchorHeight; } 47 /// Return the anchor height; this determines the vertical shift relative to the anchor position 48 final int anchorHeight() const { return mAnchorHeight; } 49 50 /// Set the side of the parent window at which popup will appear 51 final void setSide(Side popupSide) { mSide = popupSide; } 52 /// Return the side of the parent window at which popup will appear 53 final Side side() const { return mSide; } 54 55 /// Return the parent window of the popup 56 final Window parentWindow() { return mParentWindow; } 57 /// Return the parent window of the popup 58 final parentWindow() const { return mParentWindow; } 59 60 /// Invoke the associated layout generator to properly place child widgets, if any 61 override void performLayout(NVGContext nvg) 62 { 63 if (mLayout || mChildren.length != 1) { 64 Widget.performLayout(nvg); 65 } else { 66 mChildren[0].position(Vector2i(0, 0)); 67 mChildren[0].size(mSize); 68 mChildren[0].performLayout(nvg); 69 } 70 if (mSide == Side.Left) 71 mAnchorPos[0] -= size[0]; 72 } 73 74 /// Draw the popup window 75 override void draw(NVGContext nvg) 76 { 77 import arsd.nanovega; 78 import nanogui.common; 79 80 refreshRelativePlacement(); 81 82 if (!mVisible) 83 return; 84 85 int ds = mTheme.mWindowDropShadowSize, cr = mTheme.mWindowCornerRadius; 86 87 nvg.save; 88 nvg.resetScissor; 89 90 /* Draw a drop shadow */ 91 NVGPaint shadowPaint = nvg.boxGradient( 92 mPos.x, mPos.y, mSize.x, mSize.y, cr*2, ds*2, 93 mTheme.mDropShadow, mTheme.mTransparent); 94 95 nvg.beginPath; 96 nvg.rect(mPos.x-ds,mPos.y-ds, mSize.x+2*ds, mSize.y+2*ds); 97 nvg.roundedRect(mPos.x, mPos.y, mSize.x, mSize.y, cr); 98 nvg.pathWinding(NVGSolidity.Hole); 99 nvg.fillPaint(shadowPaint); 100 nvg.fill; 101 102 /* Draw window */ 103 nvg.beginPath; 104 nvg.roundedRect(mPos.x, mPos.y, mSize.x, mSize.y, cr); 105 106 Vector2i base = mPos + Vector2i(0, mAnchorHeight); 107 int sign = -1; 108 if (mSide == Side.Left) { 109 base.x += mSize.x; 110 sign = 1; 111 } 112 113 nvg.moveTo(base.x + 15*sign, base.y); 114 nvg.lineTo(base.x - 1*sign, base.y - 15); 115 nvg.lineTo(base.x - 1*sign, base.y + 15); 116 117 nvg.fillColor(mTheme.mWindowPopup); 118 nvg.fill; 119 nvg.restore; 120 121 Widget.draw(nvg); 122 } 123 124 //virtual void save(Serializer &s) const override; 125 //virtual bool load(Serializer &s) override; 126 protected: 127 /// Internal helper function to maintain nested window position values 128 override void refreshRelativePlacement() 129 { 130 mParentWindow.refreshRelativePlacement(); 131 mVisible &= mParentWindow.visibleRecursive; 132 mPos = mParentWindow.position + mAnchorPos - Vector2i(0, mAnchorHeight); 133 } 134 135 Window mParentWindow; 136 Vector2i mAnchorPos; 137 int mAnchorHeight; 138 Side mSide; 139 }