1 /// 2 module nanogui.vscrollpanel; 3 4 /* 5 nanogui/vscrollpanel.h -- Adds a vertical scrollbar around a widget 6 that is too big to fit into a certain area 7 8 NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>. 9 The widget drawing code is based on the NanoVG demo application 10 by Mikko Mononen. 11 12 All rights reserved. Use of this source code is governed by a 13 BSD-style license that can be found in the LICENSE.txt file. 14 */ 15 import std.algorithm : min, max; 16 import nanogui.widget; 17 import nanogui.common : MouseButton, Vector2f, Vector2i, NanoContext; 18 19 /** 20 * Adds a vertical scrollbar around a widget that is too big to fit into 21 * a certain area. 22 */ 23 class VScrollPanel : Widget { 24 public: 25 this(Widget parent) 26 { 27 super(parent); 28 mChildPreferredHeight = 0; 29 mScroll = 0.0f; 30 mUpdateLayout = false; 31 } 32 33 /// Return the current scroll amount as a value between 0 and 1. 0 means scrolled to the top and 1 to the bottom. 34 float scroll() const { return mScroll; } 35 /// Set the scroll amount to a value between 0 and 1. 0 means scrolled to the top and 1 to the bottom. 36 void setScroll(float scroll) { mScroll = scroll; } 37 38 override void performLayout(NanoContext ctx) 39 { 40 super.performLayout(ctx); 41 42 if (mChildren.empty) 43 return; 44 if (mChildren.length > 1) 45 throw new Exception("VScrollPanel should have one child."); 46 47 Widget child = mChildren[0]; 48 mChildPreferredHeight = child.preferredSize(ctx).y; 49 50 if (mChildPreferredHeight > mSize.y) 51 { 52 auto y = cast(int) (-mScroll*(mChildPreferredHeight - mSize.y)); 53 child.position(Vector2i(0, y)); 54 child.size(Vector2i(mSize.x-12, mChildPreferredHeight)); 55 } 56 else 57 { 58 child.position(Vector2i(0, 0)); 59 child.size(mSize); 60 mScroll = 0; 61 } 62 child.performLayout(ctx); 63 } 64 65 override Vector2i preferredSize(NanoContext ctx) const 66 { 67 if (mChildren.empty) 68 return Vector2i(0, 0); 69 return mChildren[0].preferredSize(ctx) + Vector2i(12, 0); 70 } 71 72 override bool mouseDragEvent(Vector2i p, Vector2i rel, MouseButton button, int modifiers) 73 { 74 if (!mChildren.empty && mChildPreferredHeight > mSize.y) { 75 float scrollh = height * 76 min(1.0f, height / cast(float)mChildPreferredHeight); 77 78 mScroll = max(cast(float) 0.0f, min(cast(float) 1.0f, 79 mScroll + rel.y / cast(float)(mSize.y - 8 - scrollh))); 80 mUpdateLayout = true; 81 return true; 82 } else { 83 return super.mouseDragEvent(p, rel, button, modifiers); 84 } 85 } 86 87 override bool scrollEvent(Vector2i p, Vector2f rel) 88 { 89 if (!mChildren.empty && mChildPreferredHeight > mSize.y) 90 { 91 const scrollAmount = rel.y * (mSize.y / 20.0f); 92 float scrollh = height * 93 min(1.0f, height / cast(float)mChildPreferredHeight); 94 95 mScroll = max(cast(float) 0.0f, min(cast(float) 1.0f, 96 mScroll - scrollAmount / cast(float)(mSize.y - 8 - scrollh))); 97 mUpdateLayout = true; 98 return true; 99 } else { 100 return super.scrollEvent(p, rel); 101 } 102 } 103 104 override void draw(ref NanoContext ctx) 105 { 106 if (mChildren.empty) 107 return; 108 Widget child = mChildren[0]; 109 auto y = cast(int) (-mScroll*(mChildPreferredHeight - mSize.y)); 110 child.position(Vector2i(0, y)); 111 mChildPreferredHeight = child.preferredSize(ctx).y; 112 float scrollh = height * 113 min(1.0f, height / cast(float) mChildPreferredHeight); 114 115 if (mUpdateLayout) 116 child.performLayout(ctx); 117 118 ctx.save; 119 ctx.translate(mPos.x, mPos.y); 120 ctx.intersectScissor(0, 0, mSize.x, mSize.y); 121 if (child.visible) 122 child.draw(ctx); 123 ctx.restore; 124 125 if (mChildPreferredHeight <= mSize.y) 126 return; 127 128 NVGPaint paint = ctx.boxGradient( 129 mPos.x + mSize.x - 12 + 1, mPos.y + 4 + 1, 8, 130 mSize.y - 8, 3, 4, Color(0, 0, 0, 32), Color(0, 0, 0, 92)); 131 ctx.beginPath; 132 ctx.roundedRect(mPos.x + mSize.x - 12, mPos.y + 4, 8, 133 mSize.y - 8, 3); 134 ctx.fillPaint(paint); 135 ctx.fill; 136 137 paint = ctx.boxGradient( 138 mPos.x + mSize.x - 12 - 1, 139 mPos.y + 4 + (mSize.y - 8 - scrollh) * mScroll - 1, 8, scrollh, 140 3, 4, Color(220, 220, 220, 100), Color(128, 128, 128, 100)); 141 142 ctx.beginPath; 143 ctx.roundedRect(mPos.x + mSize.x - 12 + 1, 144 mPos.y + 4 + 1 + (mSize.y - 8 - scrollh) * mScroll, 8 - 2, 145 scrollh - 2, 2); 146 ctx.fillPaint(paint); 147 ctx.fill; 148 } 149 // override void save(Serializer &s) const; 150 // override bool load(Serializer &s); 151 protected: 152 int mChildPreferredHeight; 153 float mScroll; 154 bool mUpdateLayout; 155 }