Text editing cursors now blink.
This commit is contained in:
parent
786353fa08
commit
e4b44d08c1
6 changed files with 46 additions and 8 deletions
|
|
@ -1638,6 +1638,7 @@ bool dvxUpdate(AppContextT *ctx) {
|
||||||
dispatchEvents(ctx);
|
dispatchEvents(ctx);
|
||||||
updateTooltip(ctx);
|
updateTooltip(ctx);
|
||||||
pollAnsiTermWidgets(ctx);
|
pollAnsiTermWidgets(ctx);
|
||||||
|
wgtUpdateCursorBlink();
|
||||||
|
|
||||||
ctx->frameCount++;
|
ctx->frameCount++;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -876,6 +876,11 @@ int32_t wgtAnsiTermRepaint(WidgetT *w, int32_t *outY, int32_t *outH);
|
||||||
// function call.
|
// function call.
|
||||||
struct AppContextT *wgtGetContext(const WidgetT *w);
|
struct AppContextT *wgtGetContext(const WidgetT *w);
|
||||||
|
|
||||||
|
// Update text cursor blink state. Call once per frame from dvxUpdate.
|
||||||
|
// Toggles the cursor visibility at 250ms intervals, matching the ANSI
|
||||||
|
// terminal cursor rate.
|
||||||
|
void wgtUpdateCursorBlink(void);
|
||||||
|
|
||||||
// Mark a widget as needing both re-layout (measure + position) and
|
// Mark a widget as needing both re-layout (measure + position) and
|
||||||
// repaint. Propagates upward to ancestors since a child's size change
|
// repaint. Propagates upward to ancestors since a child's size change
|
||||||
// can affect parent layout. Use this after structural changes (adding/
|
// can affect parent layout. Use this after structural changes (adding/
|
||||||
|
|
|
||||||
|
|
@ -404,8 +404,8 @@ void widgetComboBoxPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
|
||||||
drawTextN(d, ops, font, textX, textY, w->as.comboBox.buf + off, len, fg, bg, true);
|
drawTextN(d, ops, font, textX, textY, w->as.comboBox.buf + off, len, fg, bg, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw cursor
|
// Draw cursor (blinks at same rate as terminal cursor)
|
||||||
if (w->focused && w->enabled && !w->as.comboBox.open) {
|
if (w->focused && w->enabled && !w->as.comboBox.open && sCursorBlinkOn) {
|
||||||
int32_t cursorX = textX + (w->as.comboBox.cursorPos - off) * font->charWidth;
|
int32_t cursorX = textX + (w->as.comboBox.cursorPos - off) * font->charWidth;
|
||||||
|
|
||||||
if (cursorX >= w->x + TEXT_INPUT_PAD &&
|
if (cursorX >= w->x + TEXT_INPUT_PAD &&
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,7 @@ static inline void drawTextAccelEmbossed(DisplayT *d, const BlitOpsT *ops, const
|
||||||
// is that these are truly global — but since the GUI is single-threaded
|
// is that these are truly global — but since the GUI is single-threaded
|
||||||
// and only one mouse can exist, this is safe.
|
// and only one mouse can exist, this is safe.
|
||||||
|
|
||||||
|
extern bool sCursorBlinkOn; // text cursor blink phase (toggled by wgtUpdateCursorBlink)
|
||||||
extern bool sDebugLayout;
|
extern bool sDebugLayout;
|
||||||
extern WidgetT *sClosedPopup; // popup that was just closed (prevents immediate reopen)
|
extern WidgetT *sClosedPopup; // popup that was just closed (prevents immediate reopen)
|
||||||
extern WidgetT *sFocusedWidget; // currently focused widget across all windows
|
extern WidgetT *sFocusedWidget; // currently focused widget across all windows
|
||||||
|
|
|
||||||
|
|
@ -436,8 +436,8 @@ void widgetSpinnerPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitm
|
||||||
drawTextN(d, ops, font, textX, textY, &w->as.spinner.buf[off], len, fg, bg, true);
|
drawTextN(d, ops, font, textX, textY, &w->as.spinner.buf[off], len, fg, bg, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cursor
|
// Cursor (blinks at same rate as terminal cursor)
|
||||||
if (w->focused) {
|
if (w->focused && sCursorBlinkOn) {
|
||||||
int32_t curX = textX + (w->as.spinner.cursorPos - off) * font->charWidth;
|
int32_t curX = textX + (w->as.spinner.cursorPos - off) * font->charWidth;
|
||||||
|
|
||||||
if (curX >= w->x + SPINNER_BORDER && curX < btnX - SPINNER_PAD) {
|
if (curX >= w->x + SPINNER_BORDER && curX < btnX - SPINNER_PAD) {
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,8 @@
|
||||||
#define TEXTAREA_MIN_COLS 20
|
#define TEXTAREA_MIN_COLS 20
|
||||||
#define CLIPBOARD_MAX 4096
|
#define CLIPBOARD_MAX 4096
|
||||||
#define DBLCLICK_TICKS (CLOCKS_PER_SEC / 2)
|
#define DBLCLICK_TICKS (CLOCKS_PER_SEC / 2)
|
||||||
|
// Match the ANSI terminal cursor blink rate (CURSOR_MS in widgetAnsiTerm.c)
|
||||||
|
#define CURSOR_BLINK_MS 250
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Prototypes
|
// Prototypes
|
||||||
|
|
@ -104,6 +106,15 @@ static int32_t wordBoundaryRight(const char *buf, int32_t len, int32_t pos);
|
||||||
static char sClipboard[CLIPBOARD_MAX];
|
static char sClipboard[CLIPBOARD_MAX];
|
||||||
static int32_t sClipboardLen = 0;
|
static int32_t sClipboardLen = 0;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Cursor blink state
|
||||||
|
// ============================================================
|
||||||
|
// Shared across all text-editing widgets (TextInput, TextArea,
|
||||||
|
// ComboBox, Spinner). Matches the ANSI terminal's 250ms rate.
|
||||||
|
|
||||||
|
bool sCursorBlinkOn = true;
|
||||||
|
static clock_t sCursorBlinkTime = 0;
|
||||||
|
|
||||||
|
|
||||||
void clipboardCopy(const char *text, int32_t len) {
|
void clipboardCopy(const char *text, int32_t len) {
|
||||||
if (!text || len <= 0) {
|
if (!text || len <= 0) {
|
||||||
|
|
@ -129,6 +140,26 @@ const char *clipboardGet(int32_t *outLen) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Cursor blink
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
void wgtUpdateCursorBlink(void) {
|
||||||
|
clock_t now = clock();
|
||||||
|
clock_t interval = (clock_t)CURSOR_BLINK_MS * CLOCKS_PER_SEC / 1000;
|
||||||
|
|
||||||
|
if ((now - sCursorBlinkTime) >= interval) {
|
||||||
|
sCursorBlinkTime = now;
|
||||||
|
sCursorBlinkOn = !sCursorBlinkOn;
|
||||||
|
|
||||||
|
// Invalidate the focused widget so its cursor redraws
|
||||||
|
if (sFocusedWidget) {
|
||||||
|
wgtInvalidatePaint(sFocusedWidget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Multi-click tracking
|
// Multi-click tracking
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -2105,8 +2136,8 @@ void widgetTextAreaPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw cursor
|
// Draw cursor (blinks at same rate as terminal cursor)
|
||||||
if (w->focused) {
|
if (w->focused && sCursorBlinkOn) {
|
||||||
int32_t curDrawCol = w->as.textArea.cursorCol - w->as.textArea.scrollCol;
|
int32_t curDrawCol = w->as.textArea.cursorCol - w->as.textArea.scrollCol;
|
||||||
int32_t curDrawRow = w->as.textArea.cursorRow - w->as.textArea.scrollRow;
|
int32_t curDrawRow = w->as.textArea.cursorRow - w->as.textArea.scrollRow;
|
||||||
|
|
||||||
|
|
@ -2457,8 +2488,8 @@ void widgetTextInputPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bi
|
||||||
drawTextN(d, ops, font, textX, textY, dispBuf, dispLen, fg, bg, true);
|
drawTextN(d, ops, font, textX, textY, dispBuf, dispLen, fg, bg, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw cursor
|
// Draw cursor (blinks at same rate as terminal cursor)
|
||||||
if (w->focused && w->enabled) {
|
if (w->focused && w->enabled && sCursorBlinkOn) {
|
||||||
int32_t cursorX = textX + (w->as.textInput.cursorPos - off) * font->charWidth;
|
int32_t cursorX = textX + (w->as.textInput.cursorPos - off) * font->charWidth;
|
||||||
|
|
||||||
if (cursorX >= w->x + TEXT_INPUT_PAD &&
|
if (cursorX >= w->x + TEXT_INPUT_PAD &&
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue