From 8db198a468e1f7116e4b5c467e970f4b573399ec Mon Sep 17 00:00:00 2001 From: Qijia Liu Date: Thu, 8 Feb 2024 02:42:52 -0500 Subject: [PATCH] fix: flicker on bottom, sticky dragging, no highlight color (#12) --- include/webview_candidate_window.hpp | 9 +++- page/global.d.ts | 4 +- page/index.html | 2 +- page/ux.ts | 10 ++--- src/webview_candidate_window.mm | 64 ++++++++++++++++++++-------- tests/global.d.ts | 2 +- tests/test-generic.spec.ts | 2 +- tests/util.ts | 4 +- 8 files changed, 64 insertions(+), 33 deletions(-) diff --git a/include/webview_candidate_window.hpp b/include/webview_candidate_window.hpp index 2001265..be68ea6 100644 --- a/include/webview_candidate_window.hpp +++ b/include/webview_candidate_window.hpp @@ -29,12 +29,19 @@ class WebviewCandidateWindow : public CandidateWindow { void hide() override; void update_accent_color(); + void set_accent_color(); private: void set_transparent_background(); webview::webview w_; void *listener_; - bool first_draw_ = true; + double cursor_x_ = 0; + double cursor_y_ = 0; + double x_ = 0; + double y_ = 0; + bool hidden_ = true; + bool was_above_ = false; + bool accent_color_nil_ = false; int accent_color_ = 0; private: diff --git a/page/global.d.ts b/page/global.d.ts index ea1a27f..808553a 100644 --- a/page/global.d.ts +++ b/page/global.d.ts @@ -2,13 +2,13 @@ declare global { interface Window { // C++ APIs that api.ts calls _select: (index: number) => void - _resize: (x: number, y: number, width: number, height: number) => void + _resize: (dx: number, dy: number, width: number, height: number, dragging: boolean) => void // JavaScript APIs that webview_candidate_window.mm calls setCandidates: (cands: string[], labels: string[], highlighted: number) => void setLayout: (layout: 0 | 1) => void updateInputPanel: (preeditHTML: string, auxUpHTML: string, auxDownHTML: string) => void - resize: (x: number, y: number) => void + resize: (dx: number, dy: number, dragging: boolean) => void setTheme: (theme: 0 | 1 | 2) => void setAccentColor: (color: number | null) => void } diff --git a/page/index.html b/page/index.html index 987c075..a242ab7 100644 --- a/page/index.html +++ b/page/index.html @@ -12,7 +12,7 @@ -
+
diff --git a/page/ux.ts b/page/ux.ts index f597bed..2dd8792 100644 --- a/page/ux.ts +++ b/page/ux.ts @@ -3,18 +3,14 @@ import { candidates } from './selector' -let cursorX = 0 -let cursorY = 0 let pressed = false let dragging = false let startX = 0 let startY = 0 -export function resize (x: number, y: number) { - cursorX = x - cursorY = y +export function resize (dx: number, dy: number, dragging: boolean) { const rect = panel.getBoundingClientRect() - window._resize(x, y, rect.width, rect.height) + window._resize(dx, dy, rect.width, rect.height, dragging) } document.addEventListener('mousedown', e => { @@ -29,7 +25,7 @@ document.addEventListener('mousemove', e => { } dragging = true // minus because macOS has bottom-left (0, 0) - resize(cursorX + (e.clientX - startX), cursorY - (e.clientY - startY)) + resize(e.clientX - startX, -(e.clientY - startY), true) }) document.addEventListener('mouseup', e => { diff --git a/src/webview_candidate_window.mm b/src/webview_candidate_window.mm index b98827f..0e91bce 100644 --- a/src/webview_candidate_window.mm +++ b/src/webview_candidate_window.mm @@ -16,6 +16,7 @@ @implementation NotificationListener - (void)accentColorChanged:(NSNotification *)notification { self.candidateWindow->update_accent_color(); + self.candidateWindow->set_accent_color(); } @end @@ -38,26 +39,38 @@ - (void)accentColorChanged:(NSNotification *)notification { selector:@selector(accentColorChanged:) name:@"AppleColorPreferencesChangedNotification" object:nil]; + update_accent_color(); - bind("_resize", [this](double x, double y, double width, double height) { + bind("_resize", [this](double dx, double dy, double width, double height, + bool dragging) { const int gap = 4; const int preedit_height = 24; int screen_width = [[NSScreen mainScreen] frame].size.width; - if (x + width > screen_width) { - x = screen_width - width; - } - if (x < 0) { - x = 0; - } - if (height + gap > y) { // No enough space underneath - y = std::max(y + preedit_height + gap, 0); + if (dragging) { + x_ += dx; + y_ += dy; } else { - y -= height + gap; + x_ = cursor_x_; + y_ = cursor_y_; + if (x_ + width > screen_width) { + x_ = screen_width - width; + } + if (x_ < 0) { + x_ = 0; + } + if (height + gap > y_ // No enough space underneath + || (!hidden_ && was_above_)) { // It was above, avoid flicker + y_ = std::max(y_ + preedit_height + gap, 0); + was_above_ = true; + } else { + y_ -= height + gap; + was_above_ = false; + } } - + hidden_ = false; NSWindow *window = static_cast(w_.window()); - [window setFrame:NSMakeRect(x, y, width, height) + [window setFrame:NSMakeRect(x_, y_, width, height) display:YES animate:NO]; [window orderFront:nil]; @@ -87,9 +100,18 @@ - (void)accentColorChanged:(NSNotification *)notification { NSNumber *accentColor = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleAccentColor"]; if (accentColor == nil) { - invoke_js("setAccentColor", nil); + accent_color_nil_ = true; + } else { + accent_color_nil_ = false; + accent_color_ = [accentColor intValue]; + } +} + +void WebviewCandidateWindow::set_accent_color() { + if (accent_color_nil_) { + invoke_js("setAccentColor", nullptr); } else { - invoke_js("setAccentColor", [accentColor intValue]); + invoke_js("setAccentColor", accent_color_); } } @@ -108,17 +130,23 @@ - (void)accentColorChanged:(NSNotification *)notification { } void WebviewCandidateWindow::show(double x, double y) { - if (first_draw_) { - first_draw_ = false; - update_accent_color(); + cursor_x_ = x; + cursor_y_ = y; + // It's _resize which is called by resize that actually shows the window + if (hidden_) { + // Ideally this could be called only on first draw since we listen on + // accent color change, but the first draw may fail if webview is not + // warmed-up yet, and it won't be updated until user changes color. + set_accent_color(); } - invoke_js("resize", x, y); + invoke_js("resize", 0., 0., false); } void WebviewCandidateWindow::hide() { auto window = static_cast(w_.window()); [window orderBack:nil]; [window setIsVisible:NO]; + hidden_ = true; } static void build_html_open_tags(std::stringstream &ss, int flags) { diff --git a/tests/global.d.ts b/tests/global.d.ts index 0f627ed..781a0ae 100644 --- a/tests/global.d.ts +++ b/tests/global.d.ts @@ -1,6 +1,6 @@ declare global { type CppCall = { - resize: [number, number, number, number] + resize: [number, number, number, number, boolean] } | { select: number } diff --git a/tests/test-generic.spec.ts b/tests/test-generic.spec.ts index fed8135..ce3508e 100644 --- a/tests/test-generic.spec.ts +++ b/tests/test-generic.spec.ts @@ -22,7 +22,7 @@ test('HTML structure', async ({ page }) => { const actual = (await panel(page).evaluate(el => el.outerHTML)).replaceAll('> <', '><') const expected = ` -
+
diff --git a/tests/util.ts b/tests/util.ts index f0bf406..c7398f3 100644 --- a/tests/util.ts +++ b/tests/util.ts @@ -10,9 +10,9 @@ export async function init (page: Page) { await page.evaluate(() => { window.setTheme(2) window.cppCalls = [] - window._resize = (x: number, y: number, width: number, height: number) => { + window._resize = (dx: number, dy: number, width: number, height: number, dragging: boolean) => { window.cppCalls.push({ - resize: [x, y, width, height] + resize: [dx, dy, width, height, dragging] }) } window._select = (index: number) => {