diff --git a/src/dlangui/core/events.d b/src/dlangui/core/events.d index 5e9f9eea..0265237c 100644 --- a/src/dlangui/core/events.d +++ b/src/dlangui/core/events.d @@ -240,24 +240,44 @@ enum MouseAction : ubyte { /// mouse flag bits enum MouseFlag : ushort { - /// Ctrl key is down - Control = 0x0008, + // mouse buttons /// Left mouse button is down LButton = 0x0001, /// Middle mouse button is down MButton = 0x0010, /// Right mouse button is down RButton = 0x0002, - /// Shift key is down - Shift = 0x0004, /// X1 mouse button is down XButton1= 0x0020, /// X2 mouse button is down XButton2= 0x0040, + + // keyboard modifiers + /// Ctrl key is down + Control = 0x0008, + /// Shift key is down + Shift = 0x0004, /// Alt key is down - Alt = 0x0080 + Alt = 0x0080, } +/// mouse button +enum MouseButton : ubyte { + /// no button + None, + /// left mouse button + Left = MouseFlag.LButton, + /// right mouse button + Right = MouseFlag.RButton, + /// right mouse button + Middle = MouseFlag.MButton, + /// additional mouse button 1 + XButton1 = MouseFlag.XButton1, // additional button 1 + /// additional mouse button 2 + XButton2 = MouseFlag.XButton2, // additional button 2 +} + + /// mouse button state details struct ButtonDetails { /// Clock.currStdTime() for down event of this button (0 if button is up). @@ -297,22 +317,6 @@ struct ButtonDetails { @property ushort downFlags() { return _downFlags; } } -/// mouse button -enum MouseButton : ubyte { - /// no button - None, - /// left mouse button - Left, - /// right mouse button - Right, - /// right mouse button - Middle, - /// additional mouse button 1 - XButton1, // additional button - /// additional mouse button 2 - XButton2, // additional button -} - /// mouse event class MouseEvent { protected long _eventTimestamp; diff --git a/src/dlangui/core/types.d b/src/dlangui/core/types.d index dbafdc6c..0cdff181 100644 --- a/src/dlangui/core/types.d +++ b/src/dlangui/core/types.d @@ -41,8 +41,11 @@ module dlangui.core.types; import std.algorithm; +/// 2D point struct Point { + /// x coordinate int x; + /// y coordinate int y; this(int x0, int y0) { x = x0; @@ -50,25 +53,35 @@ struct Point { } } +/// 2D rectangle struct Rect { + /// x coordinate of top left corner int left; + /// y coordinate of top left corner int top; + /// x coordinate of bottom right corner int right; + /// y coordinate of bottom right corner int bottom; + /// returns average of left, right @property int middlex() { return (left + right) / 2; } + /// returns average of top, bottom @property int middley() { return (top + bottom) / 2; } + /// add offset to horizontal and vertical coordinates void offset(int dx, int dy) { left += dx; right += dx; top += dy; bottom += dy; } + /// expand rectangle dimensions void expand(int dx, int dy) { left -= dx; right += dx; top -= dy; bottom += dy; } + /// shrink rectangle dimensions void shrink(int dx, int dy) { left += dx; right -= dx; @@ -97,6 +110,7 @@ struct Rect { @property bool empty() { return right <= left || bottom <= top; } + /// translate rectangle coordinates by (x,y) - add deltax to x coordinates, and deltay to y coordinates void moveBy(int deltax, int deltay) { left += deltax; right += deltax; @@ -187,10 +201,13 @@ class RefCountedObject { ~this() {} } +/// reference counting struct Ref(T) { // if (T is RefCountedObject) private T _data; alias get this; + /// returns true if object is not assigned @property bool isNull() const { return _data is null; } + /// returns counter of references @property int refCount() const { return _data !is null ? _data.refCount : 0; } this(T data) { _data = data; @@ -231,15 +248,18 @@ struct Ref(T) { // if (T is RefCountedObject) _data.addRef(); return this; } + /// clears reference void clear() { if (_data !is null) { _data.releaseRef(); _data = null; } } + /// returns object reference (null if not assigned) @property T get() { return _data; } + /// returns const reference from const object @property const(T) get() const { return _data; } @@ -249,8 +269,10 @@ struct Ref(T) { // if (T is RefCountedObject) } } - +//================================================================================ // some utility functions + + string fromStringz(const(char[]) s) { if (s is null) return null; diff --git a/src/dlangui/widgets/grid.d b/src/dlangui/widgets/grid.d index 314b32d6..85a27d64 100644 --- a/src/dlangui/widgets/grid.d +++ b/src/dlangui/widgets/grid.d @@ -125,6 +125,11 @@ class StringGridWidget : GridWidgetBase { protected int _scrollCol; /// row scroll offset, relative to last fixed row; 0 = not scrolled protected int _scrollRow; + /// selected cell column + protected int _col; + /// selected cell row + protected int _row; + this(string ID = null) { super(ID); _headerCols = 1; @@ -135,6 +140,13 @@ class StringGridWidget : GridWidgetBase { addChild(_hscrollbar); styleId = "EDIT_BOX"; resize(20, 30); + _col = 3; + _row = 4; + for (int y = 1; y < _rows; y++) { + for (int x = 1; x < _cols; x++) { + _data[y][x] = "cell("d ~ to!dstring(x) ~ ","d ~ to!dstring(y) ~ ")"d; + } + } } @property override int cols() { return _cols; @@ -202,7 +214,7 @@ class StringGridWidget : GridWidgetBase { for (int i = _cols; i < cols; i++) { if (i >= _headerCols) _data[0][i] = genColHeader(i - _headerCols); - _colWidths[i] = i == 0 ? 20 : 80; + _colWidths[i] = i == 0 ? 20 : 100; } _rowHeights.length = rows; int fontHeight = font.height; @@ -254,6 +266,82 @@ class StringGridWidget : GridWidgetBase { } return rc; } + + /// converts client rect relative coordinates to cell coordinates + bool pointToCell(int x, int y, ref int col, ref int row, ref Rect cellRect) { + col = row = -1; + cellRect = Rect(); + Rect rc; + int xx = 0; + for (int i = 0; i < _cols; i++) { + rc.left = xx; + xx += colWidth(i); + rc.right = xx; + if (rc.left < rc.right && x >= rc.left && x < rc.right) { + col = i; + break; + } + if (xx > x) + break; + } + int yy = 0; + for (int i = 0; i < _rows; i++) { + rc.top = yy; + yy += rowHeight(i); + rc.bottom = yy; + + if (rc.top < rc.bottom && y >= rc.top && y < rc.bottom) { + row = i; + break; + } + if (yy > y) + break; + } + if (col >= 0 && row >= 0) { + cellRect = rc; + return true; + } + return false; + } + + /// handle mouse wheel events + override bool onMouseEvent(MouseEvent event) { + if (visibility != Visibility.Visible) + return false; + int c, r; // col, row + Rect rc; + bool cellFound = false; + bool normalCell = false; + // convert coordinates + if (event.action == MouseAction.ButtonUp || event.action == MouseAction.ButtonDown || event.action == MouseAction.Move) { + int x = event.x; + int y = event.y; + x -= _clientRect.left; + y -= _clientRect.top; + cellFound = pointToCell(x, y, c, r, rc); + normalCell = c >= _fixedCols && r >= _fixedRows; + } + if (event.action == MouseAction.ButtonDown && event.button == MouseButton.Left) { + if (cellFound && normalCell) { + _col = c; + _row = r; + invalidate(); + } + return true; + } + if (event.action == MouseAction.Move && (event.flags & MouseFlag.LButton)) { + // TODO: selection + if (cellFound && normalCell) { + _col = c; + _row = r; + invalidate(); + } + return true; + } + return super.onMouseEvent(event); + } + + /// returns row header title dstring rowTitle(int row) { return _rowTitles[row]; @@ -331,7 +419,7 @@ class StringGridWidget : GridWidgetBase { /// draw cell content void drawCell(DrawBuf buf, Rect rc, int col, int row) { - rc.shrink(1, 1); + rc.shrink(2, 1); FontRef fnt = font; dstring txt = cellText(col, row); Point sz = fnt.textSize(txt); @@ -351,15 +439,23 @@ class StringGridWidget : GridWidgetBase { vborder.left = vborder.right - 1; hborder.top = hborder.bottom - 1; hborder.right--; + bool selectedCol = _col == col; + bool selectedRow = _row == row; + bool selectedCell = selectedCol && selectedRow; if (col < _headerCols || row < _headerRows) { // draw header cell background - buf.fillRect(rc, 0x80808080); - buf.fillRect(vborder, 0x80FFFFFF); - buf.fillRect(hborder, 0x80FFFFFF); + uint cl = 0x80909090; + if (selectedCol || selectedRow) + cl = 0x80FFC040; + buf.fillRect(rc, cl); + buf.fillRect(vborder, 0x80202020); + buf.fillRect(hborder, 0x80202020); } else { // normal cell background buf.fillRect(vborder, 0x80C0C0C0); buf.fillRect(hborder, 0x80C0C0C0); + if (selectedCell) + buf.drawFrame(rc, 0x404040FF, Rect(1,1,1,1), 0xC0FFFF00); } }