diff --git a/PortabilityLayer/PLEditboxWidget.cpp b/PortabilityLayer/PLEditboxWidget.cpp index 1cfa96b..84676d2 100644 --- a/PortabilityLayer/PLEditboxWidget.cpp +++ b/PortabilityLayer/PLEditboxWidget.cpp @@ -29,6 +29,8 @@ namespace PortabilityLayer , m_hasFocus(false) , m_caratTimer(0) , m_isMultiLine(false) + , m_isDraggingSelection(false) + , m_dragSelectionStartChar(false) { } @@ -163,12 +165,15 @@ namespace PortabilityLayer WidgetHandleState_t EditboxWidget::ProcessEvent(const TimeTaggedVOSEvent &evt) { + if (m_isDraggingSelection) + return HandleDragSelection(evt); + if (!m_visible || !m_enabled) return WidgetHandleStates::kIgnored; if (evt.m_vosEvent.m_eventType == GpVOSEventTypes::kKeyboardInput) { - if (!m_hasFocus) + if (!m_hasFocus || m_isDraggingSelection) return WidgetHandleStates::kIgnored; const GpKeyboardInputEvent &keyEvent = evt.m_vosEvent.m_event.m_keyboardInputEvent; @@ -245,6 +250,21 @@ namespace PortabilityLayer } else if (evt.m_vosEvent.m_eventType == GpVOSEventTypes::kMouseInput) { + const GpMouseInputEvent &mouseEvent = evt.m_vosEvent.m_event.m_mouseInputEvent; + + if (evt.IsLMouseDownEvent()) + { + const Point pt = m_window->MouseToLocal(evt.m_vosEvent.m_event.m_mouseInputEvent); + + if (m_rect.Contains(pt)) + { + m_window->FocusWidget(this); + m_isDraggingSelection = true; + return HandleDragSelection(evt); + } + else + return WidgetHandleStates::kIgnored; + } } return WidgetHandleStates::kIgnored; @@ -489,15 +509,13 @@ namespace PortabilityLayer Redraw(); } - size_t EditboxWidget::FindVerticalMovementCaratPos(const Vec2i &desiredPos, bool &outShouldUnlock) const + size_t EditboxWidget::FindVerticalMovementCaratPos(const Vec2i &desiredPos, bool &isOutOfRange) const { - assert(m_isMultiLine); - Vec2i basePoint = Vec2i(0, 0); if (desiredPos.m_y < basePoint.m_y) { - outShouldUnlock = true; + isOutOfRange = true; return 0; } @@ -514,11 +532,16 @@ namespace PortabilityLayer if (characteristics.m_glyphStartPos.m_y == desiredPos.m_y) { - caratChar = characteristics.m_characterIndex; - if (characteristics.m_character == '\r') - caratChar--; foundLine = true; + caratChar = characteristics.m_characterIndex; + + if (desiredPos.m_x <= 0) + break; + + if (characteristics.m_character != '\r') + caratChar++; + if (characteristics.m_glyphStartPos.m_x <= desiredPos.m_x && characteristics.m_glyphEndPos.m_x > desiredPos.m_x) { int32_t distanceToEnd = characteristics.m_glyphEndPos.m_x - desiredPos.m_x; @@ -536,11 +559,11 @@ namespace PortabilityLayer if (foundLine) { - outShouldUnlock = false; + isOutOfRange = false; return caratChar; } - outShouldUnlock = true; + isOutOfRange = true; return m_length; } @@ -567,6 +590,70 @@ namespace PortabilityLayer } } + WidgetHandleState_t EditboxWidget::HandleDragSelection(const TimeTaggedVOSEvent &evt) + { + if (evt.m_vosEvent.m_eventType == GpVOSEventTypes::kMouseInput) + { + const GpMouseInputEvent &mouseEvent = evt.m_vosEvent.m_event.m_mouseInputEvent; + + const Point pt = m_window->MouseToLocal(evt.m_vosEvent.m_event.m_mouseInputEvent); + + RenderedFont *rfont = GetRenderedFont(); + const int32_t linegap = rfont->GetMetrics().m_linegap; + + const Vec2i basePoint = ResolveBasePoint(); + const Vec2i relativePoint = Vec2i(pt.h, pt.v) - basePoint; + + int32_t relativeY = relativePoint.m_y; + int32_t paragraph = 0; + if (relativeY >= 0) + paragraph = relativeY / linegap; + else + paragraph = -((-relativeY + (linegap - 1)) / linegap); + + bool isOutOfRange = false; + const size_t caratPos = FindVerticalMovementCaratPos(Vec2i(relativePoint.m_x, paragraph * linegap), isOutOfRange); + + if (mouseEvent.m_eventType == GpMouseEventTypes::kDown) + { + m_dragSelectionStartChar = caratPos; + m_selStartChar = caratPos; + m_selEndChar = caratPos; + m_caratSelectionAnchor = CaratSelectionAnchor_End; + + m_caratTimer = 0; + Redraw(); + } + else + { + if (caratPos < m_dragSelectionStartChar) + { + m_caratSelectionAnchor = CaratSelectionAnchor_Start; + m_selStartChar = caratPos; + m_selEndChar = m_dragSelectionStartChar; + } + else + { + m_caratSelectionAnchor = CaratSelectionAnchor_End; + m_selEndChar = caratPos; + m_selStartChar = m_dragSelectionStartChar; + } + + m_caratTimer = 0; + Redraw(); + + if (mouseEvent.m_eventType == GpMouseEventTypes::kUp) + { + m_caratScrollLocked = false; + m_isDraggingSelection = false; + return WidgetHandleStates::kDigested; + } + } + } + + return WidgetHandleStates::kCaptured; + } + void EditboxWidget::DrawSelection(DrawSurface *surface, const Vec2i &basePoint) const { PortabilityLayer::RenderedFont *rfont = surface->ResolveFont(true); diff --git a/PortabilityLayer/PLEditboxWidget.h b/PortabilityLayer/PLEditboxWidget.h index cc2ffcd..96be815 100644 --- a/PortabilityLayer/PLEditboxWidget.h +++ b/PortabilityLayer/PLEditboxWidget.h @@ -57,6 +57,8 @@ namespace PortabilityLayer size_t FindVerticalMovementCaratPos(const Vec2i &desiredPos, bool &isOutOfRange) const; void HandleKeyMoveCarat(size_t newPos, bool shiftHeld); + WidgetHandleState_t HandleDragSelection(const TimeTaggedVOSEvent &evt); + void DrawSelection(DrawSurface *surface, const Vec2i &basePoint) const; Vec2i ResolveCaratPos(const Vec2i &basePoint, PortabilityLayer::RenderedFont *rfont) const; @@ -78,6 +80,9 @@ namespace PortabilityLayer bool m_hasFocus; bool m_isMultiLine; + bool m_isDraggingSelection; + size_t m_dragSelectionStartChar; + uint16_t m_caratTimer; }; }