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, NanoContext; 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(NanoContext ctx) 62 { 63 if (mLayout || mChildren.length != 1) { 64 Widget.performLayout(ctx); 65 } else { 66 mChildren[0].position(Vector2i(0, 0)); 67 mChildren[0].size(mSize); 68 mChildren[0].performLayout(ctx); 69 } 70 if (mSide == Side.Left) 71 mAnchorPos[0] -= size[0]; 72 } 73 74 /// Draw the popup window 75 override void draw(ref NanoContext ctx) 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 ctx.save; 88 ctx.resetScissor; 89 90 /* Draw a drop shadow */ 91 NVGPaint shadowPaint = ctx.boxGradient( 92 mPos.x, mPos.y, mSize.x, mSize.y, cr*2, ds*2, 93 mTheme.mDropShadow, mTheme.mTransparent); 94 95 ctx.beginPath; 96 ctx.rect(mPos.x-ds,mPos.y-ds, mSize.x+2*ds, mSize.y+2*ds); 97 ctx.roundedRect(mPos.x, mPos.y, mSize.x, mSize.y, cr); 98 ctx.pathWinding(NVGSolidity.Hole); 99 ctx.fillPaint(shadowPaint); 100 ctx.fill; 101 102 /* Draw window */ 103 ctx.beginPath; 104 ctx.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 ctx.moveTo(base.x + 15*sign, base.y); 114 ctx.lineTo(base.x - 1*sign, base.y - 15); 115 ctx.lineTo(base.x - 1*sign, base.y + 15); 116 117 ctx.fillColor(mTheme.mWindowPopup); 118 ctx.fill; 119 ctx.restore; 120 121 Widget.draw(ctx); 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 }