diff --git a/examples/spreadsheet/src/dlangui/widgets/spreadsheet.d b/examples/spreadsheet/src/dlangui/widgets/spreadsheet.d index e9de0d83..24a38048 100644 --- a/examples/spreadsheet/src/dlangui/widgets/spreadsheet.d +++ b/examples/spreadsheet/src/dlangui/widgets/spreadsheet.d @@ -46,6 +46,8 @@ class SpreadSheetView : StringGridWidget { super(ID); layoutWidth = FILL_PARENT; layoutHeight = FILL_PARENT; + styleId = null; + backgroundColor = 0xFFFFFF; resize(50, 50); _colWidths[0] = 50; for (int i = 0; i < 26; i++) { diff --git a/examples/spreadsheet/views/res/mdpi/tab_sheet_down_hover.9.png b/examples/spreadsheet/views/res/mdpi/tab_sheet_down_hover.9.png new file mode 100644 index 00000000..f119332f Binary files /dev/null and b/examples/spreadsheet/views/res/mdpi/tab_sheet_down_hover.9.png differ diff --git a/examples/spreadsheet/views/res/mdpi/tab_sheet_down_normal.9.png b/examples/spreadsheet/views/res/mdpi/tab_sheet_down_normal.9.png index 105ef441..51b17f26 100644 Binary files a/examples/spreadsheet/views/res/mdpi/tab_sheet_down_normal.9.png and b/examples/spreadsheet/views/res/mdpi/tab_sheet_down_normal.9.png differ diff --git a/examples/spreadsheet/views/res/mdpi/tab_sheet_down_selected.9.png b/examples/spreadsheet/views/res/mdpi/tab_sheet_down_selected.9.png index c0b9d31a..d472f815 100644 Binary files a/examples/spreadsheet/views/res/mdpi/tab_sheet_down_selected.9.png and b/examples/spreadsheet/views/res/mdpi/tab_sheet_down_selected.9.png differ diff --git a/examples/spreadsheet/views/res/tab_sheet_down.xml b/examples/spreadsheet/views/res/tab_sheet_down.xml index 96315e86..046b0028 100644 --- a/examples/spreadsheet/views/res/tab_sheet_down.xml +++ b/examples/spreadsheet/views/res/tab_sheet_down.xml @@ -6,6 +6,9 @@ + diff --git a/examples/spreadsheet/views/res/theme_custom.xml b/examples/spreadsheet/views/res/theme_custom.xml index a05008df..872e55e0 100644 --- a/examples/spreadsheet/views/res/theme_custom.xml +++ b/examples/spreadsheet/views/res/theme_custom.xml @@ -13,7 +13,7 @@ diff --git a/examples/spreadsheet/views/resources.list b/examples/spreadsheet/views/resources.list index 4aa19aed..1d148c47 100644 --- a/examples/spreadsheet/views/resources.list +++ b/examples/spreadsheet/views/resources.list @@ -17,6 +17,7 @@ res/mdpi/edit-redo.png res/mdpi/edit-undo.png res/mdpi/edit-unindent.png res/mdpi/tab_sheet_down_background.9.png +res/mdpi/tab_sheet_down_hover.9.png res/mdpi/tab_sheet_down_normal.9.png res/mdpi/tab_sheet_down_selected.9.png res/mdpi/text-dml.png diff --git a/src/dlangui/widgets/grid.d b/src/dlangui/widgets/grid.d index 74a59663..683c81c1 100644 --- a/src/dlangui/widgets/grid.d +++ b/src/dlangui/widgets/grid.d @@ -215,6 +215,13 @@ interface CustomGridCellAdapter { void drawCell(DrawBuf buf, Rect rc, int col, int row); } +interface GridModelAdapter { + @property int fixedCols(); + @property int fixedRows(); + @property void fixedCols(int value); + @property void fixedRows(int value); +} + /// Callback for handling of cell selection interface CellSelectedHandler { void onCellSelected(GridWidgetBase source, int col, int row); @@ -231,7 +238,7 @@ interface ViewScrolledHandler { } /// Abstract grid widget -class GridWidgetBase : ScrollWidgetBase { +class GridWidgetBase : ScrollWidgetBase, GridModelAdapter { /// Callback to handle selection change Listener!CellSelectedHandler cellSelected; /// cellSelected signal alias for backward compatibility; will be deprecated in future @@ -252,6 +259,11 @@ class GridWidgetBase : ScrollWidgetBase { /// Set adapter to override drawing of some particular cells @property GridWidgetBase customCellAdapter(CustomGridCellAdapter adapter) { _customCellAdapter = adapter; return this; } + protected GridModelAdapter _gridModelAdapter; + /// Get adapter to hold grid model data + @property GridModelAdapter gridModelAdapter() { return _gridModelAdapter; } + /// Set adapter to hold grid model data + @property GridWidgetBase gridModelAdapter(GridModelAdapter adapter) { _gridModelAdapter = adapter; return this; } /// column count (including header columns and fixed columns) protected int _cols; @@ -327,11 +339,23 @@ class GridWidgetBase : ScrollWidgetBase { } /// fixed (non-scrollable) data column count - @property int fixedCols() { return _fixedCols; } - @property GridWidgetBase fixedCols(int c) { _fixedCols = c; invalidate(); return this; } + @property int fixedCols() { return _gridModelAdapter is null ? _fixedCols : _gridModelAdapter.fixedCols; } + @property void fixedCols(int c) { + if (_gridModelAdapter is null) + _fixedCols = c; + else + _gridModelAdapter.fixedCols = c; + invalidate(); + } /// fixed (non-scrollable) data row count - @property int fixedRows() { return _fixedRows; } - @property GridWidgetBase fixedRows(int r) { _fixedRows = r; invalidate(); return this; } + @property int fixedRows() { return _gridModelAdapter is null ? _fixedRows : _gridModelAdapter.fixedCols; } + @property void fixedRows(int r) { + if (_gridModelAdapter is null) + _fixedRows = r; + else + _gridModelAdapter.fixedCols = r; + invalidate(); + } /// default column width - for newly added columns @property int defColumnWidth() { @@ -417,7 +441,7 @@ class GridWidgetBase : ScrollWidgetBase { /// returns column width (index includes col/row headers, if any); returns 0 for columns hidden by scroll at the left int colWidth(int x) { - if (x >= _headerCols + _fixedCols && x < _headerCols + _fixedCols + _scrollCol) + if (x >= _headerCols + fixedCols && x < _headerCols + fixedCols + _scrollCol) return 0; return _colWidths[x]; } @@ -428,7 +452,7 @@ class GridWidgetBase : ScrollWidgetBase { /// returns row height (index includes col/row headers, if any); returns 0 for riws hidden by scroll at the top int rowHeight(int y) { - if (y >= _headerRows + _fixedRows && y < _headerRows + _fixedRows + _scrollRow) + if (y >= _headerRows + fixedRows && y < _headerRows + fixedRows + _scrollRow) return 0; return _rowHeights[y]; } @@ -532,15 +556,15 @@ class GridWidgetBase : ScrollWidgetBase { /// move scroll position horizontally by dx, and vertically by dy; returns true if scrolled bool scrollBy(int dx, int dy) { - return scrollTo(_headerCols + _fixedCols + _scrollCol + dx, _headerRows + _fixedRows + _scrollRow + dy); + return scrollTo(_headerCols + fixedCols + _scrollCol + dx, _headerRows + fixedRows + _scrollRow + dy); } /// set scroll position to show specified cell as top left in scrollable area; col or row -1 value means no change bool scrollTo(int col, int row, GridWidgetBase source = null, bool doNotify = true) { int oldx = _scrollCol; int oldy = _scrollRow; - int newScrollCol = col == -1 ? _scrollCol : col - _headerCols - _fixedCols; - int newScrollRow = row == -1 ? _scrollRow : row - _headerRows - _fixedRows; + int newScrollCol = col == -1 ? _scrollCol : col - _headerCols - fixedCols; + int newScrollRow = row == -1 ? _scrollRow : row - _headerRows - fixedRows; if (newScrollCol > _maxScrollCol) newScrollCol = _maxScrollCol; if (newScrollCol < 0) @@ -550,13 +574,13 @@ class GridWidgetBase : ScrollWidgetBase { if (newScrollRow < 0) newScrollRow = 0; //bool changed = false; - if (newScrollCol >= 0 && newScrollCol + _headerCols + _fixedCols < _cols) { + if (newScrollCol >= 0 && newScrollCol + _headerCols + fixedCols < _cols) { if (_scrollCol != newScrollCol) { _scrollCol = newScrollCol; //changed = true; } } - if (newScrollRow >= 0 && newScrollRow + _headerRows + _fixedRows < _rows) { + if (newScrollRow >= 0 && newScrollRow + _headerRows + fixedRows < _rows) { if (_scrollRow != newScrollRow) { _scrollRow = newScrollRow; //changed = true; @@ -578,7 +602,7 @@ class GridWidgetBase : ScrollWidgetBase { override bool onHScroll(ScrollEvent event) { if (event.action == ScrollAction.SliderMoved || event.action == ScrollAction.SliderReleased) { int col = colByAbsoluteX(event.position + _fullScrollableArea.left); - scrollTo(col, _scrollRow + _headerRows + _fixedRows); + scrollTo(col, _scrollRow + _headerRows + fixedRows); } else if (event.action == ScrollAction.PageUp) { dispatchAction(new Action(GridActions.ScrollPageLeft)); } else if (event.action == ScrollAction.PageDown) { @@ -595,7 +619,7 @@ class GridWidgetBase : ScrollWidgetBase { override bool onVScroll(ScrollEvent event) { if (event.action == ScrollAction.SliderMoved || event.action == ScrollAction.SliderReleased) { int row = rowByAbsoluteY(event.position + _fullScrollableArea.top); - scrollTo(_scrollCol + _headerCols + _fixedCols, row); + scrollTo(_scrollCol + _headerCols + fixedCols, row); } else if (event.action == ScrollAction.PageUp) { dispatchAction(new Action(GridActions.ScrollPageUp)); } else if (event.action == ScrollAction.PageDown) { @@ -612,26 +636,26 @@ class GridWidgetBase : ScrollWidgetBase { void makeCellVisible(int col, int row) { bool scrolled = false; Rect rc = cellRect(col, row); - if (col >= _headerCols + _fixedCols && col < _headerCols + _fixedCols + _scrollCol) { + if (col >= _headerCols + fixedCols && col < _headerCols + fixedCols + _scrollCol) { // scroll to the left - _scrollCol = col - _headerCols - _fixedCols; + _scrollCol = col - _headerCols - fixedCols; scrolled = true; } else { - while (rc.right > _clientRect.width && _scrollCol < _cols - _fixedCols - _headerCols - 1) { - if (_scrollCol == col - _headerCols - _fixedCols) + while (rc.right > _clientRect.width && _scrollCol < _cols - fixedCols - _headerCols - 1) { + if (_scrollCol == col - _headerCols - fixedCols) break; _scrollCol++; rc = cellRect(col, row); scrolled = true; } } - if (row >= _headerRows + _fixedRows && row < _headerRows + _fixedRows + _scrollRow) { + if (row >= _headerRows + fixedRows && row < _headerRows + fixedRows + _scrollRow) { // scroll to the left - _scrollRow = row - _headerRows - _fixedRows; + _scrollRow = row - _headerRows - fixedRows; scrolled = true; } else { - while (rc.bottom > _clientRect.height && _scrollRow < _rows - _fixedRows - _headerRows - 1) { - if (_scrollRow == row - _headerRows - _fixedRows) + while (rc.bottom > _clientRect.height && _scrollRow < _rows - fixedRows - _headerRows - 1) { + if (_scrollRow == row - _headerRows - fixedRows) break; _scrollRow++; rc = cellRect(col, row); @@ -642,7 +666,7 @@ class GridWidgetBase : ScrollWidgetBase { updateScrollBars(); invalidate(); if (viewScrolled.assigned) { - viewScrolled(this, _scrollCol + _headerCols + _fixedCols, _scrollRow + _headerRows + _fixedRows); + viewScrolled(this, _scrollCol + _headerCols + fixedCols, _scrollRow + _headerRows + fixedRows); } } } @@ -726,8 +750,8 @@ class GridWidgetBase : ScrollWidgetBase { /// calculate scrollable area info protected void calcScrollableAreaPos() { _maxScrollCol = _maxScrollRow = 0; - _fullyVisibleCells.left = _headerCols + _fixedCols + _scrollCol; - _fullyVisibleCells.top = _headerRows + _fixedRows + _scrollRow; + _fullyVisibleCells.left = _headerCols + fixedCols + _scrollCol; + _fullyVisibleCells.top = _headerRows + fixedRows + _scrollRow; Rect rc; int xx = 0; for (int i = 0; i < _cols && xx < _clientRect.width; i++) { @@ -764,7 +788,7 @@ class GridWidgetBase : ScrollWidgetBase { // calc scroll area in pixels xx = 0; for (int i = 0; i < _cols; i++) { - if (i == _headerCols + _fixedCols) { + if (i == _headerCols + fixedCols) { _fullScrollableArea.left = xx; } if (i == _fullyVisibleCells.left) { @@ -772,7 +796,7 @@ class GridWidgetBase : ScrollWidgetBase { } int w = _colWidths[i]; xx += w; - if (i >= _headerCols + _fixedCols) { + if (i >= _headerCols + fixedCols) { _fullScrollableArea.right = xx; } if (i >= _fullyVisibleCells.left) { @@ -780,18 +804,18 @@ class GridWidgetBase : ScrollWidgetBase { } } xx = 0; - for (int i = _cols - 1; i >= _headerCols + _fixedCols; i--) { + for (int i = _cols - 1; i >= _headerCols + fixedCols; i--) { int w = _colWidths[i]; if (xx + w > maxVisibleScrollWidth) { _fullScrollableArea.right += maxVisibleScrollWidth - xx; break; } - _maxScrollCol = i - _headerCols - _fixedCols; + _maxScrollCol = i - _headerCols - fixedCols; xx += w; } yy = 0; for (int i = 0; i < _rows; i++) { - if (i == _headerRows + _fixedRows) { + if (i == _headerRows + fixedRows) { _fullScrollableArea.top = yy; } if (i == _fullyVisibleCells.top) { @@ -799,7 +823,7 @@ class GridWidgetBase : ScrollWidgetBase { } int w = _rowHeights[i]; yy += w; - if (i >= _headerRows + _fixedRows) { + if (i >= _headerRows + fixedRows) { _fullScrollableArea.bottom = yy; } if (i >= _fullyVisibleCells.top) { @@ -807,13 +831,13 @@ class GridWidgetBase : ScrollWidgetBase { } } yy = 0; - for (int i = _rows - 1; i >= _headerRows + _fixedRows; i--) { + for (int i = _rows - 1; i >= _headerRows + fixedRows; i--) { int w = _rowHeights[i]; if (yy + w > maxVisibleScrollHeight) { _fullScrollableArea.bottom += maxVisibleScrollHeight - yy; break; } - _maxScrollRow = i - _headerRows - _fixedRows; + _maxScrollRow = i - _headerRows - fixedRows; yy += w; } // crop scroll area by client rect @@ -884,7 +908,7 @@ class GridWidgetBase : ScrollWidgetBase { return true; case GridActions.ScrollPageLeft: // scroll left cell by cell - int prevCol = _headerCols + _fixedCols + _scrollCol; + int prevCol = _headerCols + fixedCols + _scrollCol; while (_scrollCol > 0) { scrollBy(-1, 0); if (_fullyVisibleCells.right <= prevCol) @@ -893,14 +917,14 @@ class GridWidgetBase : ScrollWidgetBase { return true; case GridActions.ScrollPageRight: int prevCol = _fullyVisibleCells.right; - while (_headerCols + _fixedCols + _scrollCol < prevCol) { + while (_headerCols + fixedCols + _scrollCol < prevCol) { if (!scrollBy(1, 0)) break; } return true; case GridActions.ScrollPageUp: // scroll up line by line - int prevRow = _headerRows + _fixedRows + _scrollRow; + int prevRow = _headerRows + fixedRows + _scrollRow; while (_scrollRow > 0) { scrollBy(0, -1); if (_fullyVisibleCells.bottom <= prevRow) @@ -909,14 +933,14 @@ class GridWidgetBase : ScrollWidgetBase { return true; case GridActions.ScrollPageDown: int prevRow = _fullyVisibleCells.bottom; - while (_headerRows + _fixedRows + _scrollRow < prevRow) { + while (_headerRows + fixedRows + _scrollRow < prevRow) { if (!scrollBy(0, 1)) break; } return true; case GridActions.LineBegin: - if (_scrollCol > 0 && _col > _headerCols + _fixedCols + _scrollCol && !_rowSelect) - selectCell(_headerCols + _fixedCols + _scrollCol, _row); + if (_scrollCol > 0 && _col > _headerCols + fixedCols + _scrollCol && !_rowSelect) + selectCell(_headerCols + fixedCols + _scrollCol, _row); else { if (_scrollCol > 0) { _scrollCol = 0; @@ -942,13 +966,13 @@ class GridWidgetBase : ScrollWidgetBase { return true; case GridActions.PageBegin: if (_scrollRow > 0) - selectCell(_col, _headerRows + _fixedRows + _scrollRow); + selectCell(_col, _headerRows + fixedRows + _scrollRow); else selectCell(_col, _headerRows); return true; case GridActions.PageEnd: int found = -1; - for (int i = _fixedRows; i < _rows; i++) { + for (int i = fixedRows; i < _rows; i++) { Rect rc = cellRect(_col, i); if (rc.bottom <= _clientRect.height) found = i; @@ -1379,7 +1403,7 @@ class StringGridWidget : StringGridWidgetBase { if (_rowSelect && selectedRow) selectedCell = true; // normal cell background - if (c < _fixedCols || r < _fixedRows) { + if (c < fixedCols || r < fixedRows) { // fixed cell background buf.fillRect(rc, _fixedCellBackgroundColor); } diff --git a/src/dlangui/widgets/styles.d b/src/dlangui/widgets/styles.d index fdef49c2..d4ae98fd 100644 --- a/src/dlangui/widgets/styles.d +++ b/src/dlangui/widgets/styles.d @@ -280,6 +280,7 @@ class Style { protected DrawableAttribute[string] _customDrawables; protected uint[string] _customColors; + protected uint[string] _customLength; protected FontRef _font; protected DrawableRef _backgroundDrawable; @@ -390,6 +391,19 @@ class Style { return this; } + /// get custom length attribute + uint customLength(string id, uint defLength = 0) const { + if (id in _customLength) + return _customLength[id]; + return parentStyle.customLength(id, defLength); + } + + /// sets custom length attribute for style + Style setCustomLength(string id, uint value) { + _customLength[id] = value; + return this; + } + //=================================================== // font properties @@ -906,6 +920,13 @@ class Theme : Style { return defColor; } + /// get custom color attribute - transparent by default + override uint customLength(string id, uint defValue = 0) const { + if (id in _customLength) + return _customLength[id]; + return defValue; + } + /// returns colors to draw focus rectangle or null if no focus rect should be drawn for style @property override const(uint[]) focusRectColors() const { if (_focusRectColors) @@ -1269,6 +1290,13 @@ bool loadStyleAttributes(Style style, Element elem, bool allowStates) { uint color = decodeHexColor(colorvalue, COLOR_TRANSPARENT); if (colorid) style.setCustomColor(colorid, color); + } else if (item.tag.name.equal("length")) { + // + string lenid = attrValue(item, "id"); + string lenvalue = attrValue(item, "value"); + uint len = decodeDimension(lenvalue); + if (lenid.length > 0 && len > 0) + style.setCustomLength(lenid, len); } } return true; diff --git a/src/dlangui/widgets/tabs.d b/src/dlangui/widgets/tabs.d index cf75fffd..5c56dda3 100644 --- a/src/dlangui/widgets/tabs.d +++ b/src/dlangui/widgets/tabs.d @@ -220,6 +220,7 @@ class TabControl : WidgetGroupDefaultDrawing { protected ImageButton _moreButton; protected bool _enableCloseButton; protected TabItemWidget[] _sortedItems; + protected int _buttonOverlap; protected string _tabStyle; protected string _tabButtonStyle; @@ -264,6 +265,7 @@ class TabControl : WidgetGroupDefaultDrawing { w.setStyles(_tabButtonStyle, _tabButtonTextStyle); } } + _buttonOverlap = currentTheme.get(tabButtonStyle).customLength("overlap", 0); } /// when true, shows close buttons in tabs @@ -443,7 +445,7 @@ class TabControl : WidgetGroupDefaultDrawing { sz.y = tab.measuredHeight; if (sz.x + tab.measuredWidth > pwidth) break; - sz.x += tab.measuredWidth; + sz.x += tab.measuredWidth - _buttonOverlap; } measuredContent(parentWidth, parentHeight, sz.x, sz.y); //Log.d("tabControl.measure exit"); @@ -475,7 +477,7 @@ class TabControl : WidgetGroupDefaultDrawing { widget.visibility = Visibility.Visible; widget.measure(rc.width, rc.height); if (w + widget.measuredWidth < maxw) { - w += widget.measuredWidth; + w += widget.measuredWidth - _buttonOverlap; } else { widget.visibility = Visibility.Gone; } @@ -488,11 +490,38 @@ class TabControl : WidgetGroupDefaultDrawing { w = widget.measuredWidth; rc.right = rc.left + w; widget.layout(rc); - rc.left += w; + rc.left += w - _buttonOverlap; } //Log.d("tabControl.layout exit"); } + /// Draw widget at its position to buffer + override void onDraw(DrawBuf buf) { + if (visibility != Visibility.Visible) + return; + super.onDraw(buf); + Rect rc = _pos; + applyMargins(rc); + applyPadding(rc); + auto saver = ClipRectSaver(buf, rc); + for (int i = _children.count - 1; i >= 0; i--) { + Widget item = _children.get(i); + if (item.visibility != Visibility.Visible) + continue; + if (item.id.equal(_selectedTabId)) + continue; + item.onDraw(buf); + } + for (int i = 0; i < _children.count; i++) { + Widget item = _children.get(i); + if (item.visibility != Visibility.Visible) + continue; + if (!item.id.equal(_selectedTabId)) + continue; + item.onDraw(buf); + } + } + protected string _selectedTabId; @property string selectedTabId() const {