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.tabhost; 8 import w4d.layout.lineup, 9 w4d.layout.split, 10 w4d.parser.colorset, 11 w4d.style.rect, 12 w4d.style.scalar, 13 w4d.style.widget, 14 w4d.widget.base, 15 w4d.widget.button, 16 w4d.widget.panel, 17 w4d.exception; 18 import g4d.ft.font; 19 import std.algorithm, 20 std.array, 21 std.conv; 22 23 /// A widget of host for tabs. 24 class TabHostWidget : Widget 25 { 26 protected class TabHeaderWidget : ButtonWidget 27 { 28 const int id; 29 30 this ( int i ) 31 { 32 super(); 33 id = i; 34 35 parseColorSetsFromFile!"colorset/tabheader.yaml"( style ); 36 style.box.size.width = Scalar.None; 37 style.box.size.height = Scalar.None; 38 style.box.margins.bottom = 0.mm; 39 } 40 41 override void handleButtonPress () 42 { 43 super.handleButtonPress(); 44 activateTab( findTabWithId(id) ); 45 } 46 } 47 48 protected class TabHeaderPanelWidget : PanelWidget 49 { 50 override @property Widget[] children () 51 { 52 return _tabs.map!( x => x.header ).array.to!(Widget[]); 53 } 54 this () 55 { 56 super(); 57 setLayout!( HorizontalMonospacedSplitLayout ); 58 style.box.size.height = 10.mm; 59 } 60 } 61 62 protected class Tab 63 { 64 protected TabHeaderWidget _header; 65 protected Widget _contents; 66 67 @property header () { return _header; } 68 @property contents () { return _contents; } 69 70 @property id () { return _header.id; } 71 72 this ( int id, dstring title, Widget w ) 73 { 74 _header = new TabHeaderWidget( id ); 75 this.title = title; 76 77 _contents = w; 78 } 79 80 @property title ( dstring v ) 81 { 82 _header.loadText( v, _fontface ); 83 } 84 } 85 86 protected FontFace _fontface; 87 88 protected Tab[] _tabs; 89 protected long _activatedIndex; 90 91 protected TabHeaderPanelWidget _headers; 92 93 /// 94 override @property Widget[] children () 95 { 96 Widget[] result = [_headers]; 97 if ( auto tab = activatedTab ) { 98 result ~= tab.contents; 99 } 100 return result; 101 } 102 103 /// 104 this () 105 { 106 super(); 107 _tabs = []; 108 109 setLayout!( VerticalSplitLayout ); 110 _headers = new TabHeaderPanelWidget; 111 } 112 113 /// Changes the font of tab headers. 114 void setFontFace ( FontFace face ) 115 { 116 _fontface = face; 117 } 118 119 protected long indexOf ( in Tab t ) 120 { 121 return _tabs.countUntil!"a is b"(t); 122 } 123 protected long indexOf ( int id ) 124 { 125 return _tabs.countUntil!"a.id == b"(id); 126 } 127 128 /// A tab that is activated. 129 @property activatedTab () 130 { 131 if ( _activatedIndex >= 0 ) { 132 return _tabs[_activatedIndex.to!uint]; 133 } 134 return null; 135 } 136 137 /// Finds a tab that matched the id. 138 Tab findTabWithId ( int id ) 139 { 140 auto index = indexOf( id ); 141 return index >= 0? _tabs[index.to!uint]: null; 142 } 143 144 /// Adds the tab. 145 void addTab ( int id, dstring title, Widget contents ) 146 { 147 enforce( indexOf(id) < 0, "Id is duplicated." ); 148 149 _tabs ~= new Tab( id, title, contents ); 150 if ( _activatedIndex < 0 ) { 151 activateTab( _tabs[0] ); 152 } 153 } 154 /// Removes the tab. 155 void removeTab ( in Tab t ) 156 { 157 _tabs = _tabs.remove!( x => x is t ); 158 } 159 160 /// Activates the tab. 161 void activateTab ( Tab t ) 162 { 163 auto index = indexOf(t); 164 enforce( index >= 0, "Tab doesn't belong to this host." ); 165 166 _activatedIndex = index; 167 168 _tabs.each!( x => x.header.disableState( WidgetState.Selected ) ); 169 t.header.enableState( WidgetState.Selected ); 170 171 _context.setFocused( null ); // drop the focus focibly 172 requestLayout(); 173 } 174 175 /// 176 override @property bool trackable () { return false; } 177 /// 178 override @property bool focusable () { return false; } 179 }