editor: text hover timeout (tooltip) support

This commit is contained in:
Vadim Lopatin 2016-01-26 15:17:11 +03:00
parent 8e31308767
commit 7c435d772a
6 changed files with 104 additions and 4 deletions

View File

@ -627,6 +627,9 @@ class MouseEvent {
/// y coordinate of mouse pointer (relative to window client area)
@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
@property bool doubleClick() {
if (_action != MouseAction.ButtonDown)

View File

@ -73,6 +73,12 @@ struct Rect {
@property int middley() { return (top + bottom) / 2; }
/// returns middle point
@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
void offset(int dx, int dy) {
left += dx;

View File

@ -520,7 +520,7 @@ struct SimpleTextFormatter {
lineEndX = widths[lastWordEnd - 1];
}
// add line
dstring line = cast(dstring)text[lineStart .. lastWordEnd];
dstring line = cast(dstring)text[lineStart .. lineEnd]; //lastWordEnd];
int lineWidth = lineEndX - lineStartX;
sz.y += lineHeight;
if (sz.x < lineWidth)

View File

@ -862,6 +862,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
protected ulong _caretTimerId;
protected bool _caretBlinkingPhase;
protected long _lastBlinkStartTs;
protected void startCaretBlinking() {
if (window) {
long ts = currentTimeMillis;
@ -876,6 +877,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
invalidate();
}
}
protected void stopCaretBlinking() {
if (window) {
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
override bool onTimer(ulong id) {
if (id == _caretTimerId) {
@ -893,18 +896,25 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
invalidate();
return focused;
}
if (id == _hoverTimer) {
cancelHoverTimer();
onHoverTimeout(_hoverMousePosition, _hoverTextPosition);
return false;
}
return super.onTimer(id);
}
/// override to handle focus changes
override protected void handleFocusChange(bool focused) {
if (focused)
startCaretBlinking();
else
else {
stopCaretBlinking();
cancelHoverTimer();
}
super.handleFocusChange(focused);
}
/// returns cursor rectangle
protected Rect caretRect() {
Rect caretRc = textPosToClient(_caretPos);
@ -1546,6 +1556,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
/// handle keys
override bool onKeyEvent(KeyEvent event) {
if (focused) startCaretBlinking();
cancelHoverTimer();
bool ctrlOrAltPressed = false; //(event.flags & (KeyFlag.Control /* | KeyFlag.Alt */));
if (event.action == KeyAction.Text && event.text.length && !ctrlOrAltPressed) {
Log.d("text entered: ", event.text);
@ -1568,20 +1579,53 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
/// Handle Ctrl + Left mouse click on text
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.
override bool onMouseEvent(MouseEvent event) {
//Log.d("onMouseEvent ", id, " ", event.action, " (", event.x, ",", event.y, ")");
// 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();
cancelHoverTimer();
if (onLeftPaneMouseClick(event))
return true;
}
if (event.action == MouseAction.ButtonDown && event.button == MouseButton.Left) {
setFocus();
startCaretBlinking();
cancelHoverTimer();
if (event.doubleClick) {
selectWordByMouse(event.x - _clientRect.left, event.y - _clientRect.top);
} else {
@ -1596,16 +1640,29 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
updateCaretPositionByMouse(event.x - _clientRect.left, event.y - _clientRect.top, 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) {
cancelHoverTimer();
return true;
}
if (event.action == MouseAction.FocusOut || event.action == MouseAction.Cancel) {
cancelHoverTimer();
return true;
}
if (event.action == MouseAction.FocusIn) {
cancelHoverTimer();
return true;
}
if (event.action == MouseAction.Wheel) {
cancelHoverTimer();
uint keyFlags = event.flags & (MouseFlag.Shift | MouseFlag.Control | MouseFlag.Alt);
if (event.wheelDelta < 0) {
if (keyFlags == MouseFlag.Shift)
@ -1621,6 +1678,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
return handleAction(new Action(EditorActions.ScrollLineUp));
}
}
cancelHoverTimer();
return super.onMouseEvent(event);
}

View File

@ -31,6 +31,8 @@ enum PopupAlign : uint {
Center = 1,
/// place popup below anchor widget close to lower bound
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)
Right = 4,
/// align to specified point
@ -52,6 +54,8 @@ enum PopupFlags : uint {
CloseOnClickOutside = 1,
/// modal popup - keypresses and mouse events can be routed to this popup only
Modal = 2,
/// close popup when mouse is moved outside this popup
CloseOnMouseMoveOutside = 4,
}
/** interface - slot for onPopupCloseListener */
@ -124,6 +128,15 @@ class PopupWidget : LinearLayout {
if (anchor.alignment & PopupAlign.Point) {
r.left = anchor.x;
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 {
if (anchor.alignment & PopupAlign.Center) {
// center around center of anchor widget
@ -132,6 +145,9 @@ class PopupWidget : LinearLayout {
} else if (anchor.alignment & PopupAlign.Below) {
r.left = anchorrc.left;
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) {
r.left = anchorrc.right;
r.top = anchorrc.top;
@ -164,6 +180,16 @@ class PopupWidget : LinearLayout {
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;
}
}

View File

@ -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
@property Rect clientRect() { return _clientRect; }