Skip to content

Commit 08c410f

Browse files
committed
add paging indicators (#953)
1 parent 75bef13 commit 08c410f

10 files changed

+176
-49
lines changed

.github/workflows/commit-ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
run: brew install peripheryapp/periphery/periphery
3030

3131
- name: Check Unused Code
32-
run: periphery scan --skip-build --index-store-path build/Index.noindex/DataStore
32+
run: periphery scan --relative-results --skip-build --index-store-path build/Index.noindex/DataStore
3333

3434
- name: Upload Squirrel artifact
3535
uses: actions/upload-artifact@v4

.github/workflows/pull-request-ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
run: brew install peripheryapp/periphery/periphery
2727

2828
- name: Check Unused Code
29-
run: periphery scan --skip-build --index-store-path build/Index.noindex/DataStore
29+
run: periphery scan --relative-results --skip-build --index-store-path build/Index.noindex/DataStore
3030

3131
- name: Upload Squirrel artifact
3232
uses: actions/upload-artifact@v4

.github/workflows/release-ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
run: brew install peripheryapp/periphery/periphery
3535

3636
- name: Check Unused Code
37-
run: periphery scan --skip-build --index-store-path build/Index.noindex/DataStore
37+
run: periphery scan --relative-results --skip-build --index-store-path build/Index.noindex/DataStore
3838

3939
- name: Build changelog
4040
id: release_log

action-install.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
set -e
44

55
rime_version=latest
6-
rime_git_hash=6b1b41f
6+
rime_git_hash=2f89098
77
sparkle_version=2.6.2
88

99
rime_archive="rime-${rime_git_hash}-macOS-universal.tar.bz2"

data/squirrel.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ style:
3333
mutual_exclusive: false
3434
# Whether to use a translucent background. Only visible when background color is transparent
3535
translucency: false
36+
# Enable to show small arrows that indicates if paging up/down is possible
37+
show_paging: false
3638

3739
corner_radius: 7
3840
hilited_corner_radius: 0

sources/BridgingFunctions.swift

+23
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,26 @@ func ?=<T>(left: inout T?, right: T?) {
6262
extension NSRange {
6363
static let empty = NSRange(location: NSNotFound, length: 0)
6464
}
65+
66+
extension NSPoint {
67+
static func += (lhs: inout Self, rhs: Self) {
68+
lhs.x += rhs.x
69+
lhs.y += rhs.y
70+
}
71+
static func - (lhs: Self, rhs: Self) -> Self {
72+
Self.init(x: lhs.x - rhs.x, y: lhs.y - rhs.y)
73+
}
74+
static func -= (lhs: inout Self, rhs: Self) {
75+
lhs.x -= rhs.x
76+
lhs.y -= rhs.y
77+
}
78+
static func * (lhs: Self, rhs: CGFloat) -> Self {
79+
Self.init(x: lhs.x * rhs, y: lhs.y * rhs)
80+
}
81+
static func / (lhs: Self, rhs: CGFloat) -> Self {
82+
Self.init(x: lhs.x / rhs, y: lhs.y / rhs)
83+
}
84+
var length: CGFloat {
85+
sqrt(pow(self.x, 2) + pow(self.y, 2))
86+
}
87+
}

sources/SquirrelInputController.swift

+7-3
Original file line numberDiff line numberDiff line change
@@ -501,10 +501,13 @@ private extension SquirrelInputController {
501501
}
502502
}
503503
// swiftlint:enable identifier_name
504+
let page = Int(ctx.menu.page_no)
505+
let lastPage = ctx.menu.is_last_page
504506

505507
let selRange = NSRange(location: start.utf16Offset(in: preedit), length: preedit.utf16.distance(from: start, to: end))
506508
showPanel(preedit: inlinePreedit ? "" : preedit, selRange: selRange, caretPos: caretPos.utf16Offset(in: preedit),
507-
candidates: candidates, comments: comments, labels: labels, highlighted: Int(ctx.menu.highlighted_candidate_index))
509+
candidates: candidates, comments: comments, labels: labels, highlighted: Int(ctx.menu.highlighted_candidate_index),
510+
page: page, lastPage: lastPage)
508511
_ = rimeAPI.free_context(&ctx)
509512
} else {
510513
hidePalettes()
@@ -544,15 +547,16 @@ private extension SquirrelInputController {
544547
}
545548

546549
// swiftlint:disable:next function_parameter_count
547-
func showPanel(preedit: String, selRange: NSRange, caretPos: Int, candidates: [String], comments: [String], labels: [String], highlighted: Int) {
550+
func showPanel(preedit: String, selRange: NSRange, caretPos: Int, candidates: [String], comments: [String], labels: [String], highlighted: Int, page: Int, lastPage: Bool) {
548551
// print("[DEBUG] showPanelWithPreedit:...:")
549552
guard let client = client else { return }
550553
var inputPos = NSRect()
551554
client.attributes(forCharacterIndex: 0, lineHeightRectangle: &inputPos)
552555
if let panel = NSApp.squirrelAppDelegate.panel {
553556
panel.position = inputPos
554557
panel.inputController = self
555-
panel.update(preedit: preedit, selRange: selRange, caretPos: caretPos, candidates: candidates, comments: comments, labels: labels, highlighted: highlighted, update: true)
558+
panel.update(preedit: preedit, selRange: selRange, caretPos: caretPos, candidates: candidates, comments: comments, labels: labels,
559+
highlighted: highlighted, page: page, lastPage: lastPage, update: true)
556560
}
557561
}
558562
}

sources/SquirrelPanel.swift

+37-15
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ final class SquirrelPanel: NSPanel {
2929
private var cursorIndex: Int = 0
3030
private var scrollDirection: CGVector = .zero
3131
private var scrollTime: Date = .distantPast
32+
private var page: Int = 0
33+
private var lastPage: Bool = true
34+
private var pagingUp: Bool?
3235

3336
init(position: NSRect) {
3437
self.position = position
@@ -68,33 +71,45 @@ final class SquirrelPanel: NSPanel {
6871
override func sendEvent(_ event: NSEvent) {
6972
switch event.type {
7073
case .leftMouseDown:
71-
let (index, _) = view.click(at: mousePosition())
72-
if let index = index, index >= 0 && index < candidates.count {
74+
let (index, _, pagingUp) = view.click(at: mousePosition())
75+
if let pagingUp {
76+
self.pagingUp = pagingUp
77+
} else {
78+
self.pagingUp = nil
79+
}
80+
if let index, index >= 0 && index < candidates.count {
7381
self.index = index
7482
}
7583
case .leftMouseUp:
76-
let (index, preeditIndex) = view.click(at: mousePosition())
77-
if let preeditIndex = preeditIndex, preeditIndex >= 0 && preeditIndex < preedit.utf16.count {
84+
let (index, preeditIndex, pagingUp) = view.click(at: mousePosition())
85+
86+
if let pagingUp, pagingUp == self.pagingUp {
87+
_ = inputController?.page(up: pagingUp)
88+
} else {
89+
self.pagingUp = nil
90+
}
91+
if let preeditIndex, preeditIndex >= 0 && preeditIndex < preedit.utf16.count {
7892
if preeditIndex < caretPos {
7993
_ = inputController?.moveCaret(forward: true)
8094
} else if preeditIndex > caretPos {
8195
_ = inputController?.moveCaret(forward: false)
8296
}
8397
}
84-
if let index = index, index == self.index && index >= 0 && index < candidates.count {
98+
if let index, index == self.index && index >= 0 && index < candidates.count {
8599
_ = inputController?.selectCandidate(index)
86100
}
87101
case .mouseEntered:
88102
acceptsMouseMovedEvents = true
89103
case .mouseExited:
90104
acceptsMouseMovedEvents = false
91105
if cursorIndex != index {
92-
update(preedit: preedit, selRange: selRange, caretPos: caretPos, candidates: candidates, comments: comments, labels: labels, highlighted: index, update: false)
106+
update(preedit: preedit, selRange: selRange, caretPos: caretPos, candidates: candidates, comments: comments, labels: labels, highlighted: index, page: page, lastPage: lastPage, update: false)
93107
}
108+
pagingUp = nil
94109
case .mouseMoved:
95-
let (index, _) = view.click(at: mousePosition())
110+
let (index, _, _) = view.click(at: mousePosition())
96111
if let index = index, cursorIndex != index && index >= 0 && index < candidates.count {
97-
update(preedit: preedit, selRange: selRange, caretPos: caretPos, candidates: candidates, comments: comments, labels: labels, highlighted: index, update: false)
112+
update(preedit: preedit, selRange: selRange, caretPos: caretPos, candidates: candidates, comments: comments, labels: labels, highlighted: index, page: page, lastPage: lastPage, update: false)
98113
}
99114
case .scrollWheel:
100115
if event.phase == .began {
@@ -104,7 +119,7 @@ final class SquirrelPanel: NSPanel {
104119
if abs(scrollDirection.dx) > abs(scrollDirection.dy) && abs(scrollDirection.dx) > 10 {
105120
_ = inputController?.page(up: (scrollDirection.dx < 0) == vertical)
106121
} else if abs(scrollDirection.dx) < abs(scrollDirection.dy) && abs(scrollDirection.dy) > 10 {
107-
_ = inputController?.page(up: scrollDirection.dx > 0)
122+
_ = inputController?.page(up: scrollDirection.dy > 0)
108123
}
109124
scrollDirection = .zero
110125
// Mouse scroll wheel
@@ -141,7 +156,7 @@ final class SquirrelPanel: NSPanel {
141156

142157
// Main function to add attributes to text output from librime
143158
// swiftlint:disable:next cyclomatic_complexity function_parameter_count
144-
func update(preedit: String, selRange: NSRange, caretPos: Int, candidates: [String], comments: [String], labels: [String], highlighted index: Int, update: Bool) {
159+
func update(preedit: String, selRange: NSRange, caretPos: Int, candidates: [String], comments: [String], labels: [String], highlighted index: Int, page: Int, lastPage: Bool, update: Bool) {
145160
if update {
146161
self.preedit = preedit
147162
self.selRange = selRange
@@ -150,6 +165,8 @@ final class SquirrelPanel: NSPanel {
150165
self.comments = comments
151166
self.labels = labels
152167
self.index = index
168+
self.page = page
169+
self.lastPage = lastPage
153170
}
154171
cursorIndex = index
155172

@@ -266,7 +283,7 @@ final class SquirrelPanel: NSPanel {
266283
// text done!
267284
view.textView.textContentStorage?.attributedString = text
268285
view.textView.setLayoutOrientation(vertical ? .vertical : .horizontal)
269-
view.drawView(candidateRanges: candidateRanges, hilightedIndex: index, preeditRange: preeditRange, highlightedPreeditRange: highlightedPreeditRange)
286+
view.drawView(candidateRanges: candidateRanges, hilightedIndex: index, preeditRange: preeditRange, highlightedPreeditRange: highlightedPreeditRange, canPageUp: page > 0, canPageDown: !lastPage)
270287
show()
271288
}
272289

@@ -359,11 +376,12 @@ private extension SquirrelPanel {
359376

360377
if vertical {
361378
panelRect.size = NSSize(width: min(0.95 * screenRect.width, contentRect.height + theme.edgeInset.height * 2),
362-
height: min(0.95 * screenRect.height, contentRect.width + theme.edgeInset.width * 2))
379+
height: min(0.95 * screenRect.height, contentRect.width + theme.edgeInset.width * 2) + theme.pagingOffset)
380+
363381
// To avoid jumping up and down while typing, use the lower screen when
364382
// typing on upper, and vice versa
365383
if position.midY / screenRect.height >= 0.5 {
366-
panelRect.origin.y = position.minY - SquirrelTheme.offsetHeight - panelRect.height
384+
panelRect.origin.y = position.minY - SquirrelTheme.offsetHeight - panelRect.height + theme.pagingOffset
367385
} else {
368386
panelRect.origin.y = position.maxY + SquirrelTheme.offsetHeight
369387
}
@@ -376,7 +394,8 @@ private extension SquirrelPanel {
376394
} else {
377395
panelRect.size = NSSize(width: min(0.95 * screenRect.width, contentRect.width + theme.edgeInset.width * 2),
378396
height: min(0.95 * screenRect.height, contentRect.height + theme.edgeInset.height * 2))
379-
panelRect.origin = NSPoint(x: position.minX, y: position.minY - SquirrelTheme.offsetHeight - panelRect.height)
397+
panelRect.size.width += theme.pagingOffset
398+
panelRect.origin = NSPoint(x: position.minX - theme.pagingOffset, y: position.minY - SquirrelTheme.offsetHeight - panelRect.height)
380399
}
381400
if panelRect.maxX > screenRect.maxX {
382401
panelRect.origin.x = screenRect.maxX - panelRect.width
@@ -412,10 +431,13 @@ private extension SquirrelPanel {
412431

413432
view.frame = contentView!.bounds
414433
view.textView.frame = contentView!.bounds
434+
view.textView.frame.size.width -= theme.pagingOffset
435+
view.textView.frame.origin.x += theme.pagingOffset
415436
view.textView.textContainerInset = theme.edgeInset
416437

417438
if theme.translucency {
418439
back.frame = contentView!.bounds
440+
back.frame.size.width += theme.pagingOffset
419441
back.appearance = NSApp.effectiveAppearance
420442
back.isHidden = false
421443
} else {
@@ -434,7 +456,7 @@ private extension SquirrelPanel {
434456
view.textContentStorage.attributedString = text
435457
view.textView.setLayoutOrientation(vertical ? .vertical : .horizontal)
436458
view.drawView(candidateRanges: [NSRange(location: 0, length: text.length)], hilightedIndex: -1,
437-
preeditRange: .empty, highlightedPreeditRange: .empty)
459+
preeditRange: .empty, highlightedPreeditRange: .empty, canPageUp: false, canPageDown: false)
438460
show()
439461

440462
statusTimer?.invalidate()

sources/SquirrelTheme.swift

+10
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ final class SquirrelTheme {
6565
private(set) var vertical = false
6666
private(set) var inlinePreedit = false
6767
private(set) var inlineCandidate = false
68+
private(set) var showPaging = false
6869

6970
private var fonts = [NSFont]()
7071
private var labelFonts = [NSFont]()
@@ -182,6 +183,13 @@ final class SquirrelTheme {
182183
_candidateFormat = newTemplate
183184
}
184185
}
186+
var pagingOffset: CGFloat {
187+
if showPaging {
188+
(labelFontSize ?? fontSize ?? Self.defaultFontSize) * 1.5
189+
} else {
190+
0
191+
}
192+
}
185193

186194
func load(config: SquirrelConfig, dark: Bool) {
187195
linear ?= config.getString("style/candidate_list_layout").map { $0 == "linear" }
@@ -191,6 +199,7 @@ final class SquirrelTheme {
191199
translucency ?= config.getBool("style/translucency")
192200
mutualExclusive ?= config.getBool("style/mutual_exclusive")
193201
memorizeSize ?= config.getBool("style/memorize_size")
202+
showPaging ?= config.getBool("style/show_paging")
194203

195204
statusMessageType ?= .init(rawValue: config.getString("style/status_message_type") ?? "")
196205
candidateFormat ?= config.getString("style/candidate_format")
@@ -244,6 +253,7 @@ final class SquirrelTheme {
244253
inlineCandidate ?= config.getBool("\(prefix)/inline_candidate")
245254
translucency ?= config.getBool("\(prefix)/translucency")
246255
mutualExclusive ?= config.getBool("\(prefix)/mutual_exclusive")
256+
showPaging ?= config.getBool("\(prefix)/show_paging")
247257
candidateFormat ?= config.getString("\(prefix)/candidate_format")
248258
fontName ?= config.getString("\(prefix)/font_face")
249259
fontSize ?= config.getDouble("\(prefix)/font_point")

0 commit comments

Comments
 (0)