mirror of https://github.com/buggins/dlangui.git
editor: text hover timeout (tooltip) support
This commit is contained in:
parent
8e31308767
commit
7c435d772a
|
|
@ -627,6 +627,9 @@ class MouseEvent {
|
||||||
/// y coordinate of mouse pointer (relative to window client area)
|
/// y coordinate of mouse pointer (relative to window client area)
|
||||||
@property short y() { return _y; }
|
@property short y() { return _y; }
|
||||||
|
|
||||||
|
/// returns point for mouse cursor position
|
||||||
|
@property Point pos() { return Point(_x, _y); }
|
||||||
|
|
||||||
/// Returns true for ButtonDown event when button is pressed second time in short interval after pressing first time
|
/// Returns true for ButtonDown event when button is pressed second time in short interval after pressing first time
|
||||||
@property bool doubleClick() {
|
@property bool doubleClick() {
|
||||||
if (_action != MouseAction.ButtonDown)
|
if (_action != MouseAction.ButtonDown)
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,12 @@ struct Rect {
|
||||||
@property int middley() { return (top + bottom) / 2; }
|
@property int middley() { return (top + bottom) / 2; }
|
||||||
/// returns middle point
|
/// returns middle point
|
||||||
@property Point middle() { return Point(middlex, middley); }
|
@property Point middle() { return Point(middlex, middley); }
|
||||||
|
|
||||||
|
/// returns top left point of rectangle
|
||||||
|
@property Point topLeft() { return Point(left, top); }
|
||||||
|
/// returns bottom right point of rectangle
|
||||||
|
@property Point bottomRight() { return Point(right, bottom); }
|
||||||
|
|
||||||
/// add offset to horizontal and vertical coordinates
|
/// add offset to horizontal and vertical coordinates
|
||||||
void offset(int dx, int dy) {
|
void offset(int dx, int dy) {
|
||||||
left += dx;
|
left += dx;
|
||||||
|
|
|
||||||
|
|
@ -520,7 +520,7 @@ struct SimpleTextFormatter {
|
||||||
lineEndX = widths[lastWordEnd - 1];
|
lineEndX = widths[lastWordEnd - 1];
|
||||||
}
|
}
|
||||||
// add line
|
// add line
|
||||||
dstring line = cast(dstring)text[lineStart .. lastWordEnd];
|
dstring line = cast(dstring)text[lineStart .. lineEnd]; //lastWordEnd];
|
||||||
int lineWidth = lineEndX - lineStartX;
|
int lineWidth = lineEndX - lineStartX;
|
||||||
sz.y += lineHeight;
|
sz.y += lineHeight;
|
||||||
if (sz.x < lineWidth)
|
if (sz.x < lineWidth)
|
||||||
|
|
|
||||||
|
|
@ -862,6 +862,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
||||||
protected ulong _caretTimerId;
|
protected ulong _caretTimerId;
|
||||||
protected bool _caretBlinkingPhase;
|
protected bool _caretBlinkingPhase;
|
||||||
protected long _lastBlinkStartTs;
|
protected long _lastBlinkStartTs;
|
||||||
|
|
||||||
protected void startCaretBlinking() {
|
protected void startCaretBlinking() {
|
||||||
if (window) {
|
if (window) {
|
||||||
long ts = currentTimeMillis;
|
long ts = currentTimeMillis;
|
||||||
|
|
@ -876,6 +877,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void stopCaretBlinking() {
|
protected void stopCaretBlinking() {
|
||||||
if (window) {
|
if (window) {
|
||||||
if (_caretTimerId) {
|
if (_caretTimerId) {
|
||||||
|
|
@ -884,6 +886,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// handle timer; return true to repeat timer event after next interval, false cancel timer
|
/// handle timer; return true to repeat timer event after next interval, false cancel timer
|
||||||
override bool onTimer(ulong id) {
|
override bool onTimer(ulong id) {
|
||||||
if (id == _caretTimerId) {
|
if (id == _caretTimerId) {
|
||||||
|
|
@ -893,18 +896,25 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
||||||
invalidate();
|
invalidate();
|
||||||
return focused;
|
return focused;
|
||||||
}
|
}
|
||||||
|
if (id == _hoverTimer) {
|
||||||
|
cancelHoverTimer();
|
||||||
|
onHoverTimeout(_hoverMousePosition, _hoverTextPosition);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return super.onTimer(id);
|
return super.onTimer(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// override to handle focus changes
|
/// override to handle focus changes
|
||||||
override protected void handleFocusChange(bool focused) {
|
override protected void handleFocusChange(bool focused) {
|
||||||
if (focused)
|
if (focused)
|
||||||
startCaretBlinking();
|
startCaretBlinking();
|
||||||
else
|
else {
|
||||||
stopCaretBlinking();
|
stopCaretBlinking();
|
||||||
|
cancelHoverTimer();
|
||||||
|
}
|
||||||
super.handleFocusChange(focused);
|
super.handleFocusChange(focused);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// returns cursor rectangle
|
/// returns cursor rectangle
|
||||||
protected Rect caretRect() {
|
protected Rect caretRect() {
|
||||||
Rect caretRc = textPosToClient(_caretPos);
|
Rect caretRc = textPosToClient(_caretPos);
|
||||||
|
|
@ -1546,6 +1556,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
||||||
/// handle keys
|
/// handle keys
|
||||||
override bool onKeyEvent(KeyEvent event) {
|
override bool onKeyEvent(KeyEvent event) {
|
||||||
if (focused) startCaretBlinking();
|
if (focused) startCaretBlinking();
|
||||||
|
cancelHoverTimer();
|
||||||
bool ctrlOrAltPressed = false; //(event.flags & (KeyFlag.Control /* | KeyFlag.Alt */));
|
bool ctrlOrAltPressed = false; //(event.flags & (KeyFlag.Control /* | KeyFlag.Alt */));
|
||||||
if (event.action == KeyAction.Text && event.text.length && !ctrlOrAltPressed) {
|
if (event.action == KeyAction.Text && event.text.length && !ctrlOrAltPressed) {
|
||||||
Log.d("text entered: ", event.text);
|
Log.d("text entered: ", event.text);
|
||||||
|
|
@ -1568,20 +1579,53 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
||||||
|
|
||||||
/// Handle Ctrl + Left mouse click on text
|
/// Handle Ctrl + Left mouse click on text
|
||||||
protected void onControlClick() {
|
protected void onControlClick() {
|
||||||
|
// override to do something useful on Ctrl + Left mouse click in text
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TextPosition _hoverTextPosition;
|
||||||
|
protected Point _hoverMousePosition;
|
||||||
|
protected ulong _hoverTimer;
|
||||||
|
protected long _hoverTimeoutMillis = 800;
|
||||||
|
|
||||||
|
/// override to handle mouse hover timeout in text
|
||||||
|
protected void onHoverTimeout(Point pt, TextPosition pos) {
|
||||||
|
// override to do something useful on hover timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onHover(Point pos) {
|
||||||
|
if (_hoverMousePosition == pos)
|
||||||
|
return;
|
||||||
|
Log.d("onHover ", pos);
|
||||||
|
int x = pos.x - left - _leftPaneWidth;
|
||||||
|
int y = pos.y - top;
|
||||||
|
_hoverMousePosition = pos;
|
||||||
|
_hoverTextPosition = clientToTextPos(Point(x, y));
|
||||||
|
cancelHoverTimer();
|
||||||
|
_hoverTimer = setTimer(_hoverTimeoutMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void cancelHoverTimer() {
|
||||||
|
if (_hoverTimer) {
|
||||||
|
cancelTimer(_hoverTimer);
|
||||||
|
_hoverTimer = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// process mouse event; return true if event is processed by widget.
|
/// process mouse event; return true if event is processed by widget.
|
||||||
override bool onMouseEvent(MouseEvent event) {
|
override bool onMouseEvent(MouseEvent event) {
|
||||||
//Log.d("onMouseEvent ", id, " ", event.action, " (", event.x, ",", event.y, ")");
|
//Log.d("onMouseEvent ", id, " ", event.action, " (", event.x, ",", event.y, ")");
|
||||||
// support onClick
|
// support onClick
|
||||||
if (event.action == MouseAction.ButtonDown && event.x < _clientRect.left && event.x >= _clientRect.left - _leftPaneWidth) {
|
bool insideLeftPane = event.x < _clientRect.left && event.x >= _clientRect.left - _leftPaneWidth;
|
||||||
|
if (event.action == MouseAction.ButtonDown && insideLeftPane) {
|
||||||
setFocus();
|
setFocus();
|
||||||
|
cancelHoverTimer();
|
||||||
if (onLeftPaneMouseClick(event))
|
if (onLeftPaneMouseClick(event))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (event.action == MouseAction.ButtonDown && event.button == MouseButton.Left) {
|
if (event.action == MouseAction.ButtonDown && event.button == MouseButton.Left) {
|
||||||
setFocus();
|
setFocus();
|
||||||
startCaretBlinking();
|
startCaretBlinking();
|
||||||
|
cancelHoverTimer();
|
||||||
if (event.doubleClick) {
|
if (event.doubleClick) {
|
||||||
selectWordByMouse(event.x - _clientRect.left, event.y - _clientRect.top);
|
selectWordByMouse(event.x - _clientRect.left, event.y - _clientRect.top);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1596,16 +1640,29 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
||||||
updateCaretPositionByMouse(event.x - _clientRect.left, event.y - _clientRect.top, true);
|
updateCaretPositionByMouse(event.x - _clientRect.left, event.y - _clientRect.top, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (event.action == MouseAction.Move && event.flags == 0) {
|
||||||
|
// hover
|
||||||
|
if (focused && !insideLeftPane) {
|
||||||
|
onHover(event.pos);
|
||||||
|
} else {
|
||||||
|
cancelHoverTimer();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (event.action == MouseAction.ButtonUp && event.button == MouseButton.Left) {
|
if (event.action == MouseAction.ButtonUp && event.button == MouseButton.Left) {
|
||||||
|
cancelHoverTimer();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (event.action == MouseAction.FocusOut || event.action == MouseAction.Cancel) {
|
if (event.action == MouseAction.FocusOut || event.action == MouseAction.Cancel) {
|
||||||
|
cancelHoverTimer();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (event.action == MouseAction.FocusIn) {
|
if (event.action == MouseAction.FocusIn) {
|
||||||
|
cancelHoverTimer();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (event.action == MouseAction.Wheel) {
|
if (event.action == MouseAction.Wheel) {
|
||||||
|
cancelHoverTimer();
|
||||||
uint keyFlags = event.flags & (MouseFlag.Shift | MouseFlag.Control | MouseFlag.Alt);
|
uint keyFlags = event.flags & (MouseFlag.Shift | MouseFlag.Control | MouseFlag.Alt);
|
||||||
if (event.wheelDelta < 0) {
|
if (event.wheelDelta < 0) {
|
||||||
if (keyFlags == MouseFlag.Shift)
|
if (keyFlags == MouseFlag.Shift)
|
||||||
|
|
@ -1621,6 +1678,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
||||||
return handleAction(new Action(EditorActions.ScrollLineUp));
|
return handleAction(new Action(EditorActions.ScrollLineUp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cancelHoverTimer();
|
||||||
return super.onMouseEvent(event);
|
return super.onMouseEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ enum PopupAlign : uint {
|
||||||
Center = 1,
|
Center = 1,
|
||||||
/// place popup below anchor widget close to lower bound
|
/// place popup below anchor widget close to lower bound
|
||||||
Below = 2,
|
Below = 2,
|
||||||
|
/// place popup below anchor widget close to lower bound
|
||||||
|
Above = 16,
|
||||||
/// place popup below anchor widget close to right bound (when no space enough, align near left bound)
|
/// place popup below anchor widget close to right bound (when no space enough, align near left bound)
|
||||||
Right = 4,
|
Right = 4,
|
||||||
/// align to specified point
|
/// align to specified point
|
||||||
|
|
@ -52,6 +54,8 @@ enum PopupFlags : uint {
|
||||||
CloseOnClickOutside = 1,
|
CloseOnClickOutside = 1,
|
||||||
/// modal popup - keypresses and mouse events can be routed to this popup only
|
/// modal popup - keypresses and mouse events can be routed to this popup only
|
||||||
Modal = 2,
|
Modal = 2,
|
||||||
|
/// close popup when mouse is moved outside this popup
|
||||||
|
CloseOnMouseMoveOutside = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** interface - slot for onPopupCloseListener */
|
/** interface - slot for onPopupCloseListener */
|
||||||
|
|
@ -124,6 +128,15 @@ class PopupWidget : LinearLayout {
|
||||||
if (anchor.alignment & PopupAlign.Point) {
|
if (anchor.alignment & PopupAlign.Point) {
|
||||||
r.left = anchor.x;
|
r.left = anchor.x;
|
||||||
r.top = anchor.y;
|
r.top = anchor.y;
|
||||||
|
if (anchor.alignment & PopupAlign.Center) {
|
||||||
|
// center around center of anchor widget
|
||||||
|
r.left -= w / 2;
|
||||||
|
r.top -= h / 2;
|
||||||
|
} else if (anchor.alignment & PopupAlign.Below) {
|
||||||
|
} else if (anchor.alignment & PopupAlign.Above) {
|
||||||
|
r.top -= h;
|
||||||
|
} else if (anchor.alignment & PopupAlign.Right) {
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (anchor.alignment & PopupAlign.Center) {
|
if (anchor.alignment & PopupAlign.Center) {
|
||||||
// center around center of anchor widget
|
// center around center of anchor widget
|
||||||
|
|
@ -132,6 +145,9 @@ class PopupWidget : LinearLayout {
|
||||||
} else if (anchor.alignment & PopupAlign.Below) {
|
} else if (anchor.alignment & PopupAlign.Below) {
|
||||||
r.left = anchorrc.left;
|
r.left = anchorrc.left;
|
||||||
r.top = anchorrc.bottom;
|
r.top = anchorrc.bottom;
|
||||||
|
} else if (anchor.alignment & PopupAlign.Above) {
|
||||||
|
r.left = anchorrc.left;
|
||||||
|
r.top = anchorrc.top - h;
|
||||||
} else if (anchor.alignment & PopupAlign.Right) {
|
} else if (anchor.alignment & PopupAlign.Right) {
|
||||||
r.left = anchorrc.right;
|
r.left = anchorrc.right;
|
||||||
r.top = anchorrc.top;
|
r.top = anchorrc.top;
|
||||||
|
|
@ -164,6 +180,16 @@ class PopupWidget : LinearLayout {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (_flags & PopupFlags.CloseOnMouseMoveOutside) {
|
||||||
|
if (event.action == MouseAction.Move || event.action == MouseAction.Wheel) {
|
||||||
|
int threshold = 3;
|
||||||
|
if (event.x < _pos.left - threshold || event.x > _pos.right + threshold || event.y < _pos.top - threshold || event.y > _pos.bottom + threshold) {
|
||||||
|
Log.d("Closing popup due to PopupFlags.CloseOnMouseMoveOutside flag");
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,13 @@ class ScrollWidgetBase : WidgetGroup, OnScrollHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// vertical scrollbar mode
|
||||||
|
@property ScrollBarMode vscrollbarMode() { return _vscrollbarMode; }
|
||||||
|
@property void vscrollbarMode(ScrollBarMode m) { _vscrollbarMode = m; }
|
||||||
|
/// horizontal scrollbar mode
|
||||||
|
@property ScrollBarMode hscrollbarMode() { return _hscrollbarMode; }
|
||||||
|
@property void hscrollbarMode(ScrollBarMode m) { _hscrollbarMode = m; }
|
||||||
|
|
||||||
/// returns client area rectangle
|
/// returns client area rectangle
|
||||||
@property Rect clientRect() { return _clientRect; }
|
@property Rect clientRect() { return _clientRect; }
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue