1 module dlangui.widgets.spreadsheet; 2 3 import dlangui.core.types; 4 import dlangui.widgets.styles; 5 import dlangui.widgets.widget; 6 import dlangui.widgets.layouts; 7 import dlangui.widgets.controls; 8 import dlangui.widgets.tabs; 9 import dlangui.widgets.editors; 10 import dlangui.widgets.grid; 11 import dlangui.widgets.scrollbar; 12 13 import std.algorithm : min; 14 15 /// standard style id for Tab with Up alignment 16 immutable string STYLE_TAB_SHEET_DOWN = "TAB_SHEET_DOWN"; 17 /// standard style id for button of Tab with Up alignment 18 immutable string STYLE_TAB_SHEET_DOWN_BUTTON = "TAB_SHEET_DOWN_BUTTON"; 19 /// standard style id for button of Tab with Up alignment 20 immutable string STYLE_TAB_SHEET_DOWN_BUTTON_TEXT = "TAB_SHEET_DOWN_BUTTON_TEXT"; 21 22 class SheetTabs : TabControl { 23 /// create with ID parameter 24 this(string ID = null) { 25 super(ID, Align.Bottom); 26 setStyles(STYLE_TAB_SHEET_DOWN, STYLE_TAB_SHEET_DOWN_BUTTON, STYLE_TAB_SHEET_DOWN_BUTTON_TEXT); 27 _moreButton.visibility = Visibility.Gone; 28 } 29 } 30 31 class SheetEditControl : HorizontalLayout { 32 EditLine _edPosition; 33 EditLine _edText; 34 this(string ID = "sheetEdit") { 35 _edPosition = new EditLine("edPosition"); 36 _edText = new EditLine("edText"); 37 _edPosition.maxWidth = 100; 38 _edPosition.minWidth = 100; 39 _edText.layoutWidth = FILL_PARENT; 40 addChild(_edPosition); 41 addChild(_edText); 42 } 43 } 44 45 class SpreadSheetView : StringGridWidget { 46 import std.conv: to; 47 48 this(string ID = null) { 49 super(ID); 50 layoutWidth = FILL_PARENT; 51 layoutHeight = FILL_PARENT; 52 defRowHeight = 14; 53 defColumnWidth = 80; 54 styleId = null; 55 backgroundColor = 0xFFFFFF; 56 resize(50, 50); 57 _colWidths[0] = 50; 58 for (int i = 0; i < 26; i++) { 59 dchar[1] t; 60 t[0] = cast(dchar)('A' + i); 61 setColTitle(i, t.dup); 62 } 63 for (int i = 0; i < 50; i++) { 64 dstring label = to!dstring(i + 1); 65 setRowTitle(i, label); 66 } 67 } 68 } 69 70 class SpreadSheetWidget : WidgetGroupDefaultDrawing, OnScrollHandler, CellSelectedHandler, CellActivatedHandler, ViewScrolledHandler { 71 72 SheetEditControl _editControl; 73 SheetTabs _tabs; 74 75 ScrollBar _hScroll1; 76 ScrollBar _hScroll2; 77 ScrollBar _vScroll1; 78 ScrollBar _vScroll2; 79 80 SpreadSheetView _viewTopLeft; 81 SpreadSheetView _viewTopRight; 82 SpreadSheetView _viewBottomLeft; 83 SpreadSheetView _viewBottomRight; 84 85 SpreadSheetView[4] _views; 86 ScrollBar[4] _scrollbars; 87 88 this(string ID = "spreadsheet") { 89 _editControl = new SheetEditControl(); 90 _editControl.layoutWidth = FILL_PARENT; 91 _tabs = new SheetTabs(); 92 _tabs.layoutWidth = FILL_PARENT; 93 _tabs.addTab("Sheet1", "Sheet1"d); 94 _tabs.addTab("Sheet2", "Sheet2"d); 95 _tabs.addTab("Sheet3", "Sheet3"d); 96 layoutWidth = FILL_PARENT; 97 layoutHeight = FILL_PARENT; 98 backgroundColor = 0xdce2e8; 99 minHeight = 100; 100 101 _hScroll1 = new ScrollBar("hscroll1", Orientation.Horizontal); 102 _hScroll2 = new ScrollBar("hscroll2", Orientation.Horizontal); 103 _vScroll1 = new ScrollBar("vscroll1", Orientation.Vertical); 104 _vScroll2 = new ScrollBar("vscroll2", Orientation.Vertical); 105 106 _scrollbars[0] = _hScroll1; 107 _scrollbars[1] = _vScroll1; 108 _scrollbars[2] = _hScroll2; 109 _scrollbars[3] = _vScroll2; 110 111 _viewTopLeft = new SpreadSheetView("sheetViewTopLeft"); 112 _viewTopRight = new SpreadSheetView("sheetViewTopRight"); 113 _viewBottomLeft = new SpreadSheetView("sheetViewBottomLeft"); 114 _viewBottomRight = new SpreadSheetView("sheetViewBottomRight"); 115 116 _viewTopRight.setColWidth(0, 0); 117 _viewBottomLeft.setRowHeight(0, 0); 118 _viewBottomRight.setRowHeight(0, 0); 119 _viewBottomRight.setColWidth(0, 0); 120 121 _views[0] = _viewTopLeft; 122 _views[1] = _viewTopRight; 123 _views[2] = _viewBottomLeft; 124 _views[3] = _viewBottomRight; 125 126 _viewTopLeft.hscrollbar = _hScroll1; 127 _viewTopLeft.vscrollbar = _vScroll1; 128 _viewTopRight.hscrollbar = _hScroll2; 129 _viewTopRight.vscrollbar = _vScroll1; 130 _viewBottomLeft.hscrollbar = _hScroll1; 131 _viewBottomLeft.vscrollbar = _vScroll2; 132 _viewBottomRight.hscrollbar = _hScroll2; 133 _viewBottomRight.vscrollbar = _vScroll2; 134 135 addChildren([_hScroll1, _vScroll1, _hScroll2, _vScroll2, 136 _viewTopLeft, _viewTopRight, _viewBottomLeft, _viewBottomRight, 137 _editControl, _tabs 138 ]); 139 140 foreach(sb; _scrollbars) 141 sb.scrollEvent = this; 142 foreach(view; _views) { 143 view.cellSelected = this; 144 view.cellActivated = this; 145 view.viewScrolled = this; 146 } 147 } 148 149 /// Measure widget according to desired width and height constraints. (Step 1 of two phase layout). 150 override void measure(int parentWidth, int parentHeight) { 151 if (visibility == Visibility.Gone) { 152 return; 153 } 154 _measuredWidth = parentWidth; 155 _measuredHeight = parentHeight; 156 foreach(view; _views) 157 view.measure(parentWidth, parentHeight); 158 foreach(sb; _scrollbars) 159 sb.measure(parentWidth, parentHeight); 160 _editControl.measure(parentWidth, parentHeight); 161 _tabs.measure(parentWidth, parentHeight); 162 } 163 /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout). 164 override void layout(Rect rc) { 165 if (visibility == Visibility.Gone) { 166 return; 167 } 168 _pos = rc; 169 _needLayout = false; 170 applyMargins(rc); 171 applyPadding(rc); 172 int editHeight = _editControl.measuredHeight; 173 _editControl.layout(Rect(rc.left, rc.top, rc.right, rc.top + editHeight)); 174 rc.top += editHeight; 175 int splitWidth = 4; 176 int splitHeight = 4; 177 int hscrollHeight = _hScroll1.measuredHeight; 178 int vscrollWidth = _vScroll1.measuredWidth; 179 int tabsHeight = _tabs.measuredHeight; 180 int bottomSize = min(hscrollHeight, tabsHeight); 181 int splitx = (rc.width - vscrollWidth - splitWidth) / 2; 182 int splity = (rc.height - hscrollHeight - splitHeight) / 2; 183 _viewTopLeft.layout(Rect(rc.left, rc.top, rc.left + splitx, rc.top + splity)); 184 _viewTopRight.layout(Rect(rc.left + splitx + splitWidth, rc.top, rc.right - vscrollWidth, rc.top + splity)); 185 _viewBottomLeft.layout(Rect(rc.left, rc.top + splity + splitHeight, rc.left + splitx, rc.bottom - bottomSize)); 186 _viewBottomRight.layout(Rect(rc.left + splitx + splitWidth, rc.top + splity + splitHeight, rc.right - vscrollWidth, rc.bottom - bottomSize)); 187 int tabsWidth = splitx / 2; 188 _tabs.layout(Rect(rc.left, rc.bottom - bottomSize, rc.left + tabsWidth, rc.bottom - bottomSize + tabsHeight)); 189 190 _hScroll1.layout(Rect(rc.left + tabsWidth + splitWidth, rc.bottom - hscrollHeight, rc.left + splitx, rc.bottom)); 191 _hScroll2.layout(Rect(rc.left + splitx + splitWidth, rc.bottom - hscrollHeight, rc.right - vscrollWidth, rc.bottom)); 192 _vScroll1.layout(Rect(rc.right - vscrollWidth, rc.top, rc.right, rc.top + splity)); 193 _vScroll2.layout(Rect(rc.right - vscrollWidth, rc.top + splity + splitHeight, rc.right, rc.bottom - bottomSize)); 194 } 195 196 /// handle scroll event 197 override bool onScrollEvent(AbstractSlider source, ScrollEvent event) { 198 if (source == _hScroll1) { 199 _viewBottomLeft.onHScroll(event); 200 return _viewTopLeft.onHScroll(event); 201 } else if (source == _hScroll2) { 202 _viewBottomRight.onHScroll(event); 203 return _viewTopRight.onHScroll(event); 204 } else if (source == _vScroll1) { 205 _viewTopRight.onVScroll(event); 206 return _viewTopLeft.onVScroll(event); 207 } else if (source == _vScroll2) { 208 _viewBottomRight.onVScroll(event); 209 return _viewBottomLeft.onVScroll(event); 210 } 211 return true; 212 } 213 214 /// Callback for handling of cell selection 215 void onCellSelected(GridWidgetBase source, int col, int row) { 216 foreach(view; _views) { 217 if (source != view) 218 view.selectCell(col + view.headerCols, row + view.headerRows, false, source, false); 219 } 220 } 221 222 /// Callback for handling of cell double click or Enter key press 223 void onCellActivated(GridWidgetBase source, int col, int row) { 224 } 225 226 /// Callback for handling of view scroll (top left visible cell change) 227 void onViewScrolled(GridWidgetBase source, int col, int row) { 228 if (source == _viewTopLeft) { 229 _viewTopRight.scrollTo(-1, row, source, false); 230 _viewBottomLeft.scrollTo(col, -1, source, false); 231 } else if (source == _viewTopRight) { 232 _viewTopLeft.scrollTo(-1, row, source, false); 233 _viewBottomRight.scrollTo(col, -1, source, false); 234 } else if (source == _viewBottomLeft) { 235 _viewTopLeft.scrollTo(col, -1, source, false); 236 _viewBottomRight.scrollTo(-1, row, source, false); 237 } else if (source == _viewBottomRight) { 238 _viewTopRight.scrollTo(col, -1, source, false); 239 _viewBottomLeft.scrollTo(-1, row, source, false); 240 } 241 } 242 243 }