update actions for textbox ensure caret visible

This commit is contained in:
nakst 2021-12-05 17:30:26 +00:00
parent 3fe7464ed5
commit 73920030ff
3 changed files with 69 additions and 16 deletions

View File

@ -106,7 +106,7 @@ EsElement *UIFindHoverElementRecursively(EsElement *element, int offsetX, int of
const EsStyle *UIGetDefaultStyleVariant(const EsStyle *style, EsElement *parent);
void AccessKeysCenterHint(EsElement *element, EsMessage *message);
void UIRemoveFocusFromElement(EsElement *oldFocus);
void UIQueueEnsureVisibleMessage(EsElement *element);
void UIQueueEnsureVisibleMessage(EsElement *element, bool center);
void ColorPickerCreate(EsElement *parent, struct ColorPickerHost host, uint32_t initialColor, bool showTextbox);
void InspectorSetup(EsWindow *window);
@ -134,6 +134,7 @@ void InspectorNotifyElementContentChanged(EsElement *element);
#define UI_STATE_ANIMATING (1 << 9)
#define UI_STATE_CHECK_VISIBLE (1 << 10)
#define UI_STATE_QUEUED_ENSURE_VISIBLE (1 << 11)
#define UI_STATE_ENSURE_VISIBLE_CENTER (1 << 12)
// Behaviour modifiers:
#define UI_STATE_STRONG_PRESSED (1 << 12)
@ -2588,6 +2589,8 @@ struct Scrollbar : EsElement {
};
void ScrollbarLayout(Scrollbar *scrollbar) {
// TODO Do this as an UpdateAction?
if (scrollbar->viewportSize >= scrollbar->contentSize || scrollbar->viewportSize <= 0 || scrollbar->contentSize <= 0) {
EsElementSetDisabled(scrollbar, true);
} else {
@ -3194,12 +3197,16 @@ int ProcessPanelMessage(EsElement *element, EsMessage *message) {
}
} else if (message->type == ES_MSG_ENSURE_VISIBLE) {
if (panel->scroll.enabled[0] || panel->scroll.enabled[1]) {
EsElement *child = message->child, *e = child;
int offsetX = panel->scroll.position[0], offsetY = panel->scroll.position[1];
EsElement *child = message->ensureVisible.descendent, *e = child;
int offsetX = 0, offsetY = 0;
while (e != element) offsetX += e->offsetX, offsetY += e->offsetY, e = e->parent;
EsRectangle bounds = panel->GetBounds();
panel->scroll.SetX(offsetX + child->width / 2 - bounds.r / 2);
panel->scroll.SetY(offsetY + child->height / 2 - bounds.b / 2);
if (message->ensureVisible.center || !EsRectangleContainsAll(bounds, ES_RECT_4PD(offsetX, offsetY, child->width, child->height))) {
panel->scroll.SetX(offsetX + panel->scroll.position[0] + child->width / 2 - bounds.r / 2);
panel->scroll.SetY(offsetY + panel->scroll.position[1] + child->height / 2 - bounds.b / 2);
}
return ES_HANDLED;
} else {
// This is not a scroll container, so don't update the child element being made visible.
@ -6161,12 +6168,14 @@ bool EsElementIsFocused(EsElement *element) {
void UISendEnsureVisibleMessage(EsElement *element, EsGeneric) {
EsElement *child = element, *e = element;
bool center = element->state & UI_STATE_ENSURE_VISIBLE_CENTER;
EsAssert(element->state & UI_STATE_QUEUED_ENSURE_VISIBLE);
element->state &= ~UI_STATE_QUEUED_ENSURE_VISIBLE;
element->state &= ~(UI_STATE_QUEUED_ENSURE_VISIBLE | UI_STATE_ENSURE_VISIBLE_CENTER);
while (e->parent) {
EsMessage m = { ES_MSG_ENSURE_VISIBLE };
m.child = child;
m.ensureVisible.descendent = child;
m.ensureVisible.center = center;
e = e->parent;
if (ES_HANDLED == EsMessageSend(e, &m)) {
@ -6177,7 +6186,11 @@ void UISendEnsureVisibleMessage(EsElement *element, EsGeneric) {
EsAssert(~element->state & UI_STATE_QUEUED_ENSURE_VISIBLE);
}
void UIQueueEnsureVisibleMessage(EsElement *element) {
void UIQueueEnsureVisibleMessage(EsElement *element, bool center) {
if (center) {
element->state |= UI_STATE_ENSURE_VISIBLE_CENTER;
}
if (~element->state & UI_STATE_QUEUED_ENSURE_VISIBLE) {
element->state |= UI_STATE_QUEUED_ENSURE_VISIBLE;
UpdateAction action = {};
@ -6258,7 +6271,7 @@ void EsElementFocus(EsElement *element, uint32_t flags) {
// Ensure the element is visible.
if ((flags & ES_ELEMENT_FOCUS_ENSURE_VISIBLE) && element) {
UIQueueEnsureVisibleMessage(element);
UIQueueEnsureVisibleMessage(element, true);
}
}

View File

@ -1632,6 +1632,11 @@ struct EsMessageScroll {
int scroll, previous;
};
struct EsMessageEnsureVisible {
bool center; // If false, only center the element if it is partially obscured.
EsElement *descendent;
};
// List view messages.
struct EsMessageIterateIndex {
@ -1862,11 +1867,12 @@ struct EsMessage {
EsMessageScrollWheel scrollWheel;
EsMessageWindowActivated windowActivated;
EsMessageScroll scroll;
EsMessageEnsureVisible ensureVisible;
const EsStyle *childStyleVariant;
EsRectangle *accessKeyHintBounds;
EsPainter *painter;
EsElement *child;
EsCursorStyle cursorStyle;
EsElement *child;
// List view messages:
EsMessageIterateIndex iterateIndex;

View File

@ -2919,6 +2919,8 @@ struct EsTextbox : EsElement {
char *editStartContent;
int32_t editStartContentBytes;
bool ensureCaretVisibleQueued;
EsUICallback overlayCallback;
EsGeneric overlayData;
@ -2926,7 +2928,7 @@ struct EsTextbox : EsElement {
uintptr_t activeLineAllocated;
int32_t activeLineIndex, activeLineStart, activeLineOldBytes, activeLineBytes;
int32_t longestLine, longestLineWidth; // To set the horizontal scrollbar's size.
int32_t longestLine, longestLineWidth; // To set the horizontal scroll bar's size.
TextboxCaret carets[2]; // carets[1] is the actual caret; carets[0] is the selection anchor.
TextboxCaret wordSelectionAnchor, wordSelectionAnchor2;
@ -3341,11 +3343,22 @@ TextboxVisibleLine *TextboxGetVisibleLine(EsTextbox *textbox, int32_t documentLi
? nullptr : &textbox->visibleLines[documentLineIndex - textbox->firstVisibleLine];
}
void EsTextboxEnsureCaretVisible(EsTextbox *textbox, bool verticallyCenter) {
void TextboxEnsureCaretVisibleActionCallback(EsElement *element, EsGeneric context) {
EsTextbox *textbox = (EsTextbox *) element;
bool verticallyCenter = context.u;
TextboxCaret caret = textbox->carets[1];
EsRectangle bounds = textbox->GetBounds();
{
EsPrint("TextboxEnsureCaretVisibleActionCallback ------------\n");
for (uintptr_t i = 0; i < 3; i++) {
// ScrollPane::SetY causes ES_MSG_SCROLL_Y to get sent to the textbox.
// This causes a TextboxRefreshVisibleLines, which may cause new lines to added.
// If these lines had not been previously horizontally measured, this will then occur.
// This then causes a ScrollPane::Refresh for the new horizontal width.
// If this causes the horizontal scroll bar to appear, then the caret may no longer be fully visible.
// Therefore, we repeat up to 3 times to ensure that the caret is definitely fully visible.
EsRectangle bounds = textbox->GetBounds();
DocumentLine *line = &textbox->lines[caret.line];
int caretY = line->yPosition + textbox->insets.t;
@ -3364,13 +3377,21 @@ void EsTextboxEnsureCaretVisible(EsTextbox *textbox, bool verticallyCenter) {
}
}
textbox->scroll.SetY(scrollY);
if (textbox->scroll.position[1] != scrollY) {
EsPrint(" new scroll %d\n", scrollY);
textbox->scroll.SetY(scrollY);
} else {
break;
}
} else {
break;
}
}
TextboxVisibleLine *visibleLine = TextboxGetVisibleLine(textbox, caret.line);
if (visibleLine) {
EsRectangle bounds = textbox->GetBounds();
DocumentLine *line = &textbox->lines[caret.line];
int scrollX = textbox->scroll.position[0];
int viewportWidth = bounds.r;
@ -3386,7 +3407,19 @@ void EsTextboxEnsureCaretVisible(EsTextbox *textbox, bool verticallyCenter) {
textbox->scroll.SetX(scrollX);
}
UIQueueEnsureVisibleMessage(textbox);
UIQueueEnsureVisibleMessage(textbox, false);
textbox->ensureCaretVisibleQueued = false;
}
void EsTextboxEnsureCaretVisible(EsTextbox *textbox, bool verticallyCenter) {
if (!textbox->ensureCaretVisibleQueued) {
UpdateAction action = {};
action.element = textbox;
action.callback = TextboxEnsureCaretVisibleActionCallback;
action.context.u = verticallyCenter;
textbox->window->updateActions.Add(action);
textbox->ensureCaretVisibleQueued = true;
}
}
bool TextboxMoveCaret(EsTextbox *textbox, TextboxCaret *caret, bool right, int moveType, bool strongWhitespace = false) {
@ -3667,6 +3700,7 @@ void TextboxUndoItemCallback(const void *item, EsUndoManager *manager, EsMessage
textbox->carets[0] = header->caretsBefore[0];
textbox->carets[1] = header->caretsBefore[1];
EsTextboxInsert(textbox, (const char *) (header + 1), header->insertBytes, true);
EsTextboxEnsureCaretVisible(textbox);
} else if (message->type == ES_MSG_UNDO_CANCEL) {
// Nothing to do.
}