diff --git a/page/api.ts b/page/api.ts index 0667305..c42af2f 100644 --- a/page/api.ts +++ b/page/api.ts @@ -4,7 +4,10 @@ import { auxUp, auxDown } from './selector' -import { resize } from './ux' +import { + getHoverBehavior, + resize +} from './ux' import { setTheme, setAccentColor @@ -23,14 +26,40 @@ function setLayout (layout : 0 | 1) { } } +function moveHighlight (from: Element | null, to: Element | null) { + from?.classList.remove('highlighted') + to?.classList.add('highlighted') + const fromMark = from?.querySelector('.mark') + const toMark = to?.querySelector('.mark') + if (fromMark && !toMark) { + to?.querySelector('.candidate-inner')?.prepend(fromMark) + } +} + +function clearAdditionalHighlight () { + candidates.querySelector('.highlighted:not(.highlighted-original)')?.classList.remove('highlighted') +} + function setCandidates (cands: string[], labels: string[], highlighted: number, markText: string) { candidates.innerHTML = '' for (let i = 0; i < cands.length; ++i) { const candidate = document.createElement('div') candidate.classList.add('candidate') if (i === highlighted) { - candidate.classList.add('highlighted') + candidate.classList.add('highlighted', 'highlighted-original') } + + candidate.addEventListener('mouseenter', () => { + const hoverBehavior = getHoverBehavior() + if (hoverBehavior === 'Move') { + const lastHighlighted = candidates.querySelector('.highlighted') + moveHighlight(lastHighlighted, candidate) + } else if (hoverBehavior === 'Add') { + clearAdditionalHighlight() + candidate.classList.add('highlighted') + } + }) + const label = document.createElement('div') label.classList.add('label') label.innerHTML = labels[i] @@ -48,12 +77,11 @@ function setCandidates (cands: string[], labels: string[], highlighted: number, } else { mark.innerHTML = markText } - candidateInner.appendChild(mark) + candidateInner.append(mark) } - candidateInner.appendChild(label) - candidateInner.appendChild(text) - candidate.appendChild(candidateInner) - candidates.appendChild(candidate) + candidateInner.append(label, text) + candidate.append(candidateInner) + candidates.append(candidate) } } @@ -72,6 +100,17 @@ function updateInputPanel (preeditHTML: string, auxUpHTML: string, auxDownHTML: updateElement(auxDown, auxDownHTML) } +candidates.addEventListener('mouseleave', () => { + const hoverBehavior = getHoverBehavior() + if (hoverBehavior === 'Move') { + const lastHighlighted = candidates.querySelector('.highlighted') + const originalHighlighted = candidates.querySelector('.highlighted-original') + moveHighlight(lastHighlighted, originalHighlighted) + } else if (hoverBehavior === 'Add') { + clearAdditionalHighlight() + } +}) + setTheme(0) // JavaScript APIs that webview_candidate_window.mm calls diff --git a/page/customize.ts b/page/customize.ts index cc8cf17..f28e370 100644 --- a/page/customize.ts +++ b/page/customize.ts @@ -1,4 +1,6 @@ import { + HOVER_BEHAVIOR, + setHoverBehavior, setBlur, setBlink } from './ux' @@ -43,8 +45,9 @@ type STYLE_JSON = { Cursor: { Style: 'Blink' | 'Static' | 'Text' } - HighlightMark: { - Style: 'None' | 'Bar' | 'Text' + Highlight: { + MarkStyle: 'None' | 'Bar' | 'Text' + HoverBehavior: HOVER_BEHAVIOR } Size: { BorderWidth: string @@ -70,6 +73,7 @@ const PREEDIT = `${PANEL} .preedit` const CURSOR_NO_TEXT = `${PANEL} .cursor.no-text` const CANDIDATE_INNER = `${PANEL} .candidate-inner` const HIGHLIGHT_MARK = `${PANEL} .highlighted .mark` +const HIGHLIGHT_ORIGINAL_MARK = `${PANEL} .highlighted-original .mark` const PANEL_LIGHT = `${PANEL}.light` const PANEL_LIGHT_HIGHLIGHT = `${PANEL_LIGHT} .candidate.highlighted .candidate-inner` @@ -112,7 +116,7 @@ export function setStyle (style: string) { const j = JSON.parse(style) as STYLE_JSON const rules: {[key: string]: {[key: string]: string}} = {} const hasBackgroundImage = j.Background.ImageUrl.trim() !== '' - const markKey = j.HighlightMark.Style === 'Text' ? 'color' : 'background-color' + const markKey = j.Highlight.MarkStyle === 'Text' ? 'color' : 'background-color' rules[PANEL] = {} rules[CANDIDATES] = {} rules[TEXT] = {} @@ -120,6 +124,7 @@ export function setStyle (style: string) { rules[PREEDIT] = {} rules[CURSOR_NO_TEXT] = {} rules[HIGHLIGHT_MARK] = {} + rules[HIGHLIGHT_ORIGINAL_MARK] = {} rules[CANDIDATE_INNER] = {} if (j.LightMode.OverrideDefault === 'True') { @@ -248,7 +253,8 @@ export function setStyle (style: string) { setBlink(j.Cursor.Style === 'Blink') - rules[HIGHLIGHT_MARK].opacity = j.HighlightMark.Style === 'None' ? '0' : '1' + rules[j.Highlight.HoverBehavior === 'Add' ? HIGHLIGHT_ORIGINAL_MARK : HIGHLIGHT_MARK].opacity = j.Highlight.MarkStyle === 'None' ? '0' : '1' + setHoverBehavior(j.Highlight.HoverBehavior) rules[PANEL]['border-width'] = px(j.Size.BorderWidth) rules[PANEL]['border-radius'] = px(j.Size.BorderRadius) diff --git a/page/ux.ts b/page/ux.ts index e9acb7f..135b497 100644 --- a/page/ux.ts +++ b/page/ux.ts @@ -147,3 +147,12 @@ setInterval(() => { showCursor(blinkSwitch) blinkSwitch = !blinkSwitch }, 500) + +export type HOVER_BEHAVIOR = 'None' | 'Move' | 'Add' +let hoverBehavior: HOVER_BEHAVIOR = 'None' +export function setHoverBehavior (behavior: HOVER_BEHAVIOR) { + hoverBehavior = behavior +} +export function getHoverBehavior () { + return hoverBehavior +} diff --git a/src/webview_candidate_window.mm b/src/webview_candidate_window.mm index fdb7d36..dd7e7da 100644 --- a/src/webview_candidate_window.mm +++ b/src/webview_candidate_window.mm @@ -22,13 +22,26 @@ - (void)accentColorChanged:(NSNotification *)notification { @end +@interface HoverableWindow : NSWindow + +@end + +@implementation HoverableWindow + +- (BOOL)isKeyWindow { + return YES; +} + +@end + namespace candidate_window { WebviewCandidateWindow::WebviewCandidateWindow() - : w_(1, [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 400, 300) - styleMask:NSWindowStyleMaskBorderless - backing:NSBackingStoreBuffered - defer:NO]), + : w_(1, [[HoverableWindow alloc] + initWithContentRect:NSMakeRect(0, 0, 400, 300) + styleMask:NSWindowStyleMaskBorderless + backing:NSBackingStoreBuffered + defer:NO]), listener_([[NotificationListener alloc] init]) { [static_cast(w_.window()) setLevel:NSPopUpMenuWindowLevel]; set_transparent_background(); diff --git a/tests/test-generic.spec.ts b/tests/test-generic.spec.ts index b7965e4..196133b 100644 --- a/tests/test-generic.spec.ts +++ b/tests/test-generic.spec.ts @@ -33,7 +33,7 @@ test('HTML structure', async ({ page }) => {
-
+
1