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