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 }