mirror of https://github.com/buggins/dlangui.git
grid mouse handling - select cell
This commit is contained in:
parent
1a849c1970
commit
305e020de7
|
@ -240,24 +240,44 @@ enum MouseAction : ubyte {
|
||||||
|
|
||||||
/// mouse flag bits
|
/// mouse flag bits
|
||||||
enum MouseFlag : ushort {
|
enum MouseFlag : ushort {
|
||||||
/// Ctrl key is down
|
// mouse buttons
|
||||||
Control = 0x0008,
|
|
||||||
/// Left mouse button is down
|
/// Left mouse button is down
|
||||||
LButton = 0x0001,
|
LButton = 0x0001,
|
||||||
/// Middle mouse button is down
|
/// Middle mouse button is down
|
||||||
MButton = 0x0010,
|
MButton = 0x0010,
|
||||||
/// Right mouse button is down
|
/// Right mouse button is down
|
||||||
RButton = 0x0002,
|
RButton = 0x0002,
|
||||||
/// Shift key is down
|
|
||||||
Shift = 0x0004,
|
|
||||||
/// X1 mouse button is down
|
/// X1 mouse button is down
|
||||||
XButton1= 0x0020,
|
XButton1= 0x0020,
|
||||||
/// X2 mouse button is down
|
/// X2 mouse button is down
|
||||||
XButton2= 0x0040,
|
XButton2= 0x0040,
|
||||||
|
|
||||||
|
// keyboard modifiers
|
||||||
|
/// Ctrl key is down
|
||||||
|
Control = 0x0008,
|
||||||
|
/// Shift key is down
|
||||||
|
Shift = 0x0004,
|
||||||
/// Alt key is down
|
/// 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
|
/// mouse button state details
|
||||||
struct ButtonDetails {
|
struct ButtonDetails {
|
||||||
/// Clock.currStdTime() for down event of this button (0 if button is up).
|
/// Clock.currStdTime() for down event of this button (0 if button is up).
|
||||||
|
@ -297,22 +317,6 @@ struct ButtonDetails {
|
||||||
@property ushort downFlags() { return _downFlags; }
|
@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
|
/// mouse event
|
||||||
class MouseEvent {
|
class MouseEvent {
|
||||||
protected long _eventTimestamp;
|
protected long _eventTimestamp;
|
||||||
|
|
|
@ -41,8 +41,11 @@ module dlangui.core.types;
|
||||||
|
|
||||||
import std.algorithm;
|
import std.algorithm;
|
||||||
|
|
||||||
|
/// 2D point
|
||||||
struct Point {
|
struct Point {
|
||||||
|
/// x coordinate
|
||||||
int x;
|
int x;
|
||||||
|
/// y coordinate
|
||||||
int y;
|
int y;
|
||||||
this(int x0, int y0) {
|
this(int x0, int y0) {
|
||||||
x = x0;
|
x = x0;
|
||||||
|
@ -50,25 +53,35 @@ struct Point {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 2D rectangle
|
||||||
struct Rect {
|
struct Rect {
|
||||||
|
/// x coordinate of top left corner
|
||||||
int left;
|
int left;
|
||||||
|
/// y coordinate of top left corner
|
||||||
int top;
|
int top;
|
||||||
|
/// x coordinate of bottom right corner
|
||||||
int right;
|
int right;
|
||||||
|
/// y coordinate of bottom right corner
|
||||||
int bottom;
|
int bottom;
|
||||||
|
/// returns average of left, right
|
||||||
@property int middlex() { return (left + right) / 2; }
|
@property int middlex() { return (left + right) / 2; }
|
||||||
|
/// returns average of top, bottom
|
||||||
@property int middley() { return (top + bottom) / 2; }
|
@property int middley() { return (top + bottom) / 2; }
|
||||||
|
/// add offset to horizontal and vertical coordinates
|
||||||
void offset(int dx, int dy) {
|
void offset(int dx, int dy) {
|
||||||
left += dx;
|
left += dx;
|
||||||
right += dx;
|
right += dx;
|
||||||
top += dy;
|
top += dy;
|
||||||
bottom += dy;
|
bottom += dy;
|
||||||
}
|
}
|
||||||
|
/// expand rectangle dimensions
|
||||||
void expand(int dx, int dy) {
|
void expand(int dx, int dy) {
|
||||||
left -= dx;
|
left -= dx;
|
||||||
right += dx;
|
right += dx;
|
||||||
top -= dy;
|
top -= dy;
|
||||||
bottom += dy;
|
bottom += dy;
|
||||||
}
|
}
|
||||||
|
/// shrink rectangle dimensions
|
||||||
void shrink(int dx, int dy) {
|
void shrink(int dx, int dy) {
|
||||||
left += dx;
|
left += dx;
|
||||||
right -= dx;
|
right -= dx;
|
||||||
|
@ -97,6 +110,7 @@ struct Rect {
|
||||||
@property bool empty() {
|
@property bool empty() {
|
||||||
return right <= left || bottom <= top;
|
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) {
|
void moveBy(int deltax, int deltay) {
|
||||||
left += deltax;
|
left += deltax;
|
||||||
right += deltax;
|
right += deltax;
|
||||||
|
@ -187,10 +201,13 @@ class RefCountedObject {
|
||||||
~this() {}
|
~this() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// reference counting
|
||||||
struct Ref(T) { // if (T is RefCountedObject)
|
struct Ref(T) { // if (T is RefCountedObject)
|
||||||
private T _data;
|
private T _data;
|
||||||
alias get this;
|
alias get this;
|
||||||
|
/// returns true if object is not assigned
|
||||||
@property bool isNull() const { return _data is null; }
|
@property bool isNull() const { return _data is null; }
|
||||||
|
/// returns counter of references
|
||||||
@property int refCount() const { return _data !is null ? _data.refCount : 0; }
|
@property int refCount() const { return _data !is null ? _data.refCount : 0; }
|
||||||
this(T data) {
|
this(T data) {
|
||||||
_data = data;
|
_data = data;
|
||||||
|
@ -231,15 +248,18 @@ struct Ref(T) { // if (T is RefCountedObject)
|
||||||
_data.addRef();
|
_data.addRef();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
/// clears reference
|
||||||
void clear() {
|
void clear() {
|
||||||
if (_data !is null) {
|
if (_data !is null) {
|
||||||
_data.releaseRef();
|
_data.releaseRef();
|
||||||
_data = null;
|
_data = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// returns object reference (null if not assigned)
|
||||||
@property T get() {
|
@property T get() {
|
||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
|
/// returns const reference from const object
|
||||||
@property const(T) get() const {
|
@property const(T) get() const {
|
||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
|
@ -249,8 +269,10 @@ struct Ref(T) { // if (T is RefCountedObject)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//================================================================================
|
||||||
// some utility functions
|
// some utility functions
|
||||||
|
|
||||||
|
|
||||||
string fromStringz(const(char[]) s) {
|
string fromStringz(const(char[]) s) {
|
||||||
if (s is null)
|
if (s is null)
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -125,6 +125,11 @@ class StringGridWidget : GridWidgetBase {
|
||||||
protected int _scrollCol;
|
protected int _scrollCol;
|
||||||
/// row scroll offset, relative to last fixed row; 0 = not scrolled
|
/// row scroll offset, relative to last fixed row; 0 = not scrolled
|
||||||
protected int _scrollRow;
|
protected int _scrollRow;
|
||||||
|
/// selected cell column
|
||||||
|
protected int _col;
|
||||||
|
/// selected cell row
|
||||||
|
protected int _row;
|
||||||
|
|
||||||
this(string ID = null) {
|
this(string ID = null) {
|
||||||
super(ID);
|
super(ID);
|
||||||
_headerCols = 1;
|
_headerCols = 1;
|
||||||
|
@ -135,6 +140,13 @@ class StringGridWidget : GridWidgetBase {
|
||||||
addChild(_hscrollbar);
|
addChild(_hscrollbar);
|
||||||
styleId = "EDIT_BOX";
|
styleId = "EDIT_BOX";
|
||||||
resize(20, 30);
|
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() {
|
@property override int cols() {
|
||||||
return _cols;
|
return _cols;
|
||||||
|
@ -202,7 +214,7 @@ class StringGridWidget : GridWidgetBase {
|
||||||
for (int i = _cols; i < cols; i++) {
|
for (int i = _cols; i < cols; i++) {
|
||||||
if (i >= _headerCols)
|
if (i >= _headerCols)
|
||||||
_data[0][i] = genColHeader(i - _headerCols);
|
_data[0][i] = genColHeader(i - _headerCols);
|
||||||
_colWidths[i] = i == 0 ? 20 : 80;
|
_colWidths[i] = i == 0 ? 20 : 100;
|
||||||
}
|
}
|
||||||
_rowHeights.length = rows;
|
_rowHeights.length = rows;
|
||||||
int fontHeight = font.height;
|
int fontHeight = font.height;
|
||||||
|
@ -254,6 +266,82 @@ class StringGridWidget : GridWidgetBase {
|
||||||
}
|
}
|
||||||
return rc;
|
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
|
/// returns row header title
|
||||||
dstring rowTitle(int row) {
|
dstring rowTitle(int row) {
|
||||||
return _rowTitles[row];
|
return _rowTitles[row];
|
||||||
|
@ -331,7 +419,7 @@ class StringGridWidget : GridWidgetBase {
|
||||||
|
|
||||||
/// draw cell content
|
/// draw cell content
|
||||||
void drawCell(DrawBuf buf, Rect rc, int col, int row) {
|
void drawCell(DrawBuf buf, Rect rc, int col, int row) {
|
||||||
rc.shrink(1, 1);
|
rc.shrink(2, 1);
|
||||||
FontRef fnt = font;
|
FontRef fnt = font;
|
||||||
dstring txt = cellText(col, row);
|
dstring txt = cellText(col, row);
|
||||||
Point sz = fnt.textSize(txt);
|
Point sz = fnt.textSize(txt);
|
||||||
|
@ -351,15 +439,23 @@ class StringGridWidget : GridWidgetBase {
|
||||||
vborder.left = vborder.right - 1;
|
vborder.left = vborder.right - 1;
|
||||||
hborder.top = hborder.bottom - 1;
|
hborder.top = hborder.bottom - 1;
|
||||||
hborder.right--;
|
hborder.right--;
|
||||||
|
bool selectedCol = _col == col;
|
||||||
|
bool selectedRow = _row == row;
|
||||||
|
bool selectedCell = selectedCol && selectedRow;
|
||||||
if (col < _headerCols || row < _headerRows) {
|
if (col < _headerCols || row < _headerRows) {
|
||||||
// draw header cell background
|
// draw header cell background
|
||||||
buf.fillRect(rc, 0x80808080);
|
uint cl = 0x80909090;
|
||||||
buf.fillRect(vborder, 0x80FFFFFF);
|
if (selectedCol || selectedRow)
|
||||||
buf.fillRect(hborder, 0x80FFFFFF);
|
cl = 0x80FFC040;
|
||||||
|
buf.fillRect(rc, cl);
|
||||||
|
buf.fillRect(vborder, 0x80202020);
|
||||||
|
buf.fillRect(hborder, 0x80202020);
|
||||||
} else {
|
} else {
|
||||||
// normal cell background
|
// normal cell background
|
||||||
buf.fillRect(vborder, 0x80C0C0C0);
|
buf.fillRect(vborder, 0x80C0C0C0);
|
||||||
buf.fillRect(hborder, 0x80C0C0C0);
|
buf.fillRect(hborder, 0x80C0C0C0);
|
||||||
|
if (selectedCell)
|
||||||
|
buf.drawFrame(rc, 0x404040FF, Rect(1,1,1,1), 0xC0FFFF00);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue