1 // Written in the D programming language. 2 /++ 3 + Authors: KanzakiKino 4 + Copyright: KanzakiKino 2018 5 + License: LGPL-3.0 6 ++/ 7 module w4d.widget.scrollbar; 8 import w4d.parser.colorset, 9 w4d.style.color, 10 w4d.style.scalar, 11 w4d.task.window, 12 w4d.widget.base, 13 w4d.util.vector, 14 w4d.event; 15 import g4d.element.shape.rect, 16 g4d.shader.base; 17 import gl3n.linalg; 18 import std.algorithm; 19 20 /// A handler that handles scrolling. 21 alias ScrollHandler = EventHandler!( void, float ); 22 23 /// A widget of scroll bar. 24 class ScrollBarWidget (bool H) : Widget 25 { 26 /// Whether direction of the scroll bar is horizon. 27 alias Horizon = H; 28 29 /// Magnification of wheel. 30 enum WheelMagnification = 0.1; 31 32 /// 33 ScrollHandler onScroll; 34 35 /// Bar length. (>0) 36 protected float _barLength; 37 /// Current value. 38 protected float _value; 39 40 /// Bar element. 41 protected RectElement _bar; 42 /// Translate. 43 protected vec2 _translate; 44 45 /// Dragging offset. 46 protected float _dragOffset; 47 48 protected float getValueAt ( vec2 pos ) 49 { 50 auto size = style.box.clientSize; 51 return pos.getLength!H / size.getLength!H; 52 } 53 54 /// 55 override bool handleMouseMove ( vec2 pos ) 56 { 57 if ( super.handleMouseMove(pos) ) return true; 58 59 if ( isTracked ) { 60 pos -= style.clientLeftTop; 61 setValue( getValueAt(pos) - _dragOffset ); 62 return true; 63 } 64 return false; 65 } 66 /// 67 override bool handleMouseButton ( MouseButton btn, bool status, vec2 pos ) 68 { 69 if ( super.handleMouseButton(btn,status,pos) ) return true; 70 71 if ( btn == MouseButton.Left && status ) { 72 pos -= style.clientLeftTop; 73 74 auto newValue = getValueAt( pos ); 75 if ( newValue > _value && newValue < _value+_barLength ) { 76 _dragOffset = newValue - _value; 77 } else { 78 _dragOffset = _barLength/2f; 79 } 80 setValue( newValue - _dragOffset ); 81 return true; 82 } 83 return false; 84 } 85 /// 86 override bool handleMouseScroll ( vec2 amount, vec2 pos ) 87 { 88 if ( super.handleMouseScroll( amount, pos ) ) return true; 89 90 auto add = -amount.getLength!H * 91 _barLength * WheelMagnification; 92 setValue( _value + add ); 93 return true; 94 } 95 96 /// 97 void handleScroll ( float v ) 98 { 99 onScroll.call( v ); 100 } 101 102 /// 103 this () 104 { 105 super(); 106 107 _barLength = 1f; 108 _value = 0f; 109 110 _bar = new RectElement; 111 _translate = vec2(0,0); 112 113 parseColorSetsFromFile!"colorset/scrollbar.yaml"( style ); 114 static if ( Horizon ) { 115 style.box.size.height = 3.mm; 116 } else { 117 style.box.size.width = 3.mm; 118 } 119 } 120 121 /// Checks if drawing the bar is needed. 122 @property needDrawBar () 123 { 124 return _barLength < 1f; 125 } 126 127 /// Changes length of the bar. 128 void setBarLength ( float newlen ) 129 { 130 newlen = newlen.clamp( 0f, 1f ); 131 auto temp = _barLength; 132 _barLength = newlen; 133 134 correctBar(); 135 handleScroll( _value ); 136 137 if ( temp != _barLength ) { 138 resizeElements(); 139 } 140 } 141 /// Changes value of the bar. 142 void setValue ( float value ) 143 { 144 auto temp = _value; 145 _value = value; 146 147 if ( temp != _value ) { 148 correctBar(); 149 handleScroll( _value ); 150 resizeElements(); 151 } 152 } 153 154 protected void correctBar () 155 { 156 if ( _barLength <= 0f ) { 157 _barLength = 1f; 158 } 159 setValue( max( 0f, _value ) ); 160 if ( _value+_barLength > 1f ) { 161 setValue( 1f - _barLength ); 162 } 163 } 164 165 protected void resizeElements () 166 { 167 if ( !style.isCalced ) { 168 requestLayout(); 169 170 } else if ( needDrawBar ) { 171 auto size = style.box.clientSize; 172 auto barsize = size; 173 barsize.getLength!H *= _barLength; 174 _bar.resize( barsize ); 175 176 _translate = barsize/2; 177 _translate.getLength!H += 178 size.getLength!H * _value; 179 } 180 } 181 /// 182 override vec2 layout ( vec2 pos, vec2 size ) 183 { 184 scope(success) resizeElements(); 185 return super.layout( pos, size ); 186 } 187 188 /// 189 override void draw ( Window w, in ColorSet parent ) 190 { 191 super.draw( w, parent ); 192 if ( !needDrawBar ) return; 193 194 auto late = style.clientLeftTop + _translate; 195 196 auto shader = w.shaders.fill3; 197 auto saver = ShaderStateSaver( shader ); 198 shader.use(); 199 shader.matrix.late = vec3( late, 0 ); 200 shader.color = colorset.foreground; 201 _bar.draw( shader ); 202 } 203 204 /// 205 override @property bool trackable () { return true; } 206 /// 207 override @property bool focusable () { return true; } 208 } 209 210 /// A widget of horizontal scroll bar. 211 alias HorizontalScrollBarWidget = ScrollBarWidget!true; 212 /// A widget of vertical scroll bar. 213 alias VerticalScrollBarWidget = ScrollBarWidget!false;