Skip to content

Commit c4af554

Browse files
feat(theme): add toolbar theme config options (#1729)
* feat: make your ToolBar compiler kawaii~! * refactor: cleanup ToolBar code * feat(theme): allow users to use builtin iconfont via text notation Powered by Android-Iconics and Community Meterial Design Icons. * refactor(theme): split out `option_styles` from 'style' for option tool button --------- Co-authored-by: WhiredPlanck <whiredplanck@outlook.com>
1 parent cf21223 commit c4af554

File tree

12 files changed

+567
-63
lines changed

12 files changed

+567
-63
lines changed

app/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ dependencies {
173173
implementation(libs.splitties.views.dsl.recyclerview)
174174
implementation(libs.splitties.views.recyclerview)
175175
implementation(libs.aboutlibraries.core)
176+
implementation(libs.iconics.core)
177+
implementation(libs.community.material.typeface) {
178+
artifact { type = "aar" }
179+
}
176180

177181
// Testing
178182
testImplementation(libs.junit)

app/src/main/assets/shared/tongwenfeng.trime.yaml

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -998,7 +998,68 @@ liquid_keyboard:
998998
type: SYMBOL
999999
name: 符号表
10001000
keys: [ 符号: '/fh', 电脑: '/dn', 象棋: '/xq', 麻将: '/mj', 骰子: '/sz', 扑克: '/pk', 天气: '/tq', 音乐: '/yy', 八卦: '/bg', 易经: '/lssg', 天体: '/tt', 星座: '/xz', 星号: '/xh', 方块: '/fk', 几何: '/jh', 箭头: '/jt', 数学: '/sx', 上标: '/sb', 下标: '/xb', 单位: '/dw', 货币: '/hb', 拼音: '/py', 注音: '/zy', 假名: '/jm', 片假: '/pjm', 韩文: '/hw', 希腊: '/xl', 希大: '/xld', 罗马: '/lm', 罗大: '/lmd', 俄语: '/ey', 俄大: '/eyd', 表情: '/bq', 一: '/1', 二: '/2', 三: '/3', 四: '/4', 五: '/5', 六: '/6', 七: '/7', 八: '/8', 九: '/9', 零: '/0', 十: '/10', 分数: '/fs', 标点: '/bd', 偏旁: '/pp', 竖标: '/bdz' ]
1001-
1001+
1002+
tool_bar:
1003+
button_spacing: 5 # 按钮间距
1004+
button_font: iconfont.ttf # 按钮字体
1005+
1006+
# 左侧固定主按钮配置
1007+
primary_button:
1008+
# 背景样式设置
1009+
background:
1010+
type: circle # 高亮形状 (可选值: circle|rectangle)
1011+
corner_radius: 10 # 圆角半径
1012+
bg_normal: 0x00 # 正常状态背景颜色
1013+
bg_highlight: hilited_candidate_button_color # 高亮状态背景色
1014+
vertical_inset: 4 # 垂直方向内缩量
1015+
horizontal_inset: 0 # 水平方向内缩量
1016+
# 前景内容设置
1017+
foreground:
1018+
style: "ic@dots_horizontal" # 内置质感设计图标样式(以ic@开头)
1019+
bg_normal: candidate_text_color # 正常状态前景色 (本地图片不允许着色)
1020+
# bg_highlight: # 高亮状态前景色
1021+
padding: 10 # 前景内容与背景边界的内边距
1022+
# size: [120, 24] # 视图控件宽高,非前景样式宽高
1023+
# 按钮点击事件
1024+
action: menu_keyboard
1025+
1026+
1027+
hide_button: &hide_button
1028+
foreground:
1029+
style: "ic@keyboard_close"
1030+
bg_normal: candidate_text_color
1031+
action: Hide
1032+
1033+
emoji_button: &emoji_button
1034+
foreground:
1035+
style: "ic@emoticon"
1036+
# 前景样式可以为空,使用默认样式,按钮将显示为 action 的 label
1037+
action: liquid_keyboard_emoji
1038+
1039+
clipboard_button: &clipboard_button
1040+
foreground:
1041+
style: "ic@clipboard"
1042+
action: liquid_keyboard_clipboard
1043+
1044+
edit_button: &edit_button
1045+
foreground:
1046+
style: "ic@numeric"
1047+
action: Keyboard_numberb
1048+
1049+
ascii_mode_button: &ascii_mode_button
1050+
foreground:
1051+
option_styles: ["En", "中"] # 样式也可以为表,将根据 action 的 toggle 决定
1052+
font_size: 18 # 按钮字体大小
1053+
padding: 10
1054+
action: Mode_switch
1055+
1056+
full_shape_button: &full_shape_button
1057+
foreground:
1058+
option_styles: ["ic@moon_full", "ic@moon_new"]
1059+
action: Zenkaku_Hankaku
1060+
1061+
# 右侧按钮列表(从右向左排列)
1062+
buttons: [ *hide_button, *emoji_button, *clipboard_button, *edit_button, *ascii_mode_button, *full_shape_button ]
10021063

10031064
#键盘布局
10041065
preset_keyboards:
@@ -3966,6 +4027,7 @@ preset_keys:
39664027
liquid_keyboard_tabs: { label: 更多, send: function, command: liquid_keyboard, option: "更多" }
39674028
liquid_keyboard_emoji: { label: 🙂, send: function, command: liquid_keyboard, option: "emoji" }
39684029
liquid_keyboard_clipboard: { label: 剪贴, send: function, command: liquid_keyboard, option: "剪贴" }
4030+
menu_keyboard: { label: 菜单, send: function, command: menu_keyboard }
39694031
# rime状态
39704032
Mode_switch: {toggle: ascii_mode, functional: false, send: Mode_switch, states: [ 中文, 西文 ]}
39714033
Zenkaku_Hankaku: {toggle: full_shape, send: Mode_switch, states: [ 半角, 全角 ]}

app/src/main/java/com/osfans/trime/data/theme/FontManager.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ object FontManager {
2727
SYMBOL_FONT,
2828
TEXT_FONT,
2929
LONG_TEXT_FONT,
30+
TOOLBAR_FONT,
3031
}
3132

3233
private val fontDir get() = File(DataManager.userDataDir, "fonts")
@@ -111,6 +112,7 @@ object FontManager {
111112
FontKey.SYMBOL_FONT -> style.symbolFont
112113
FontKey.TEXT_FONT -> style.textFont
113114
FontKey.LONG_TEXT_FONT -> style.longTextFont
115+
FontKey.TOOLBAR_FONT -> theme.toolBar.buttonFont
114116
else -> null
115117
}
116118
}

app/src/main/java/com/osfans/trime/data/theme/Theme.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ import com.osfans.trime.data.base.DataManager
1313
import com.osfans.trime.data.theme.mapper.GeneralStyleMapper
1414
import com.osfans.trime.data.theme.mapper.LiquidKeyboardMapper
1515
import com.osfans.trime.data.theme.mapper.TextKeyboardMapper
16+
import com.osfans.trime.data.theme.mapper.ToolBarMapper
1617
import com.osfans.trime.data.theme.model.ColorScheme
1718
import com.osfans.trime.data.theme.model.GeneralStyle
1819
import com.osfans.trime.data.theme.model.LiquidKeyboard
1920
import com.osfans.trime.data.theme.model.PresetKey
2021
import com.osfans.trime.data.theme.model.TextKeyboard
22+
import com.osfans.trime.data.theme.model.ToolBar
2123
import com.osfans.trime.util.getString
2224
import kotlinx.parcelize.Parcelize
2325
import timber.log.Timber
@@ -34,6 +36,7 @@ data class Theme(
3436
val presetKeyboards: Map<String, TextKeyboard>,
3537
val colorSchemes: List<ColorScheme>,
3638
val fallbackColors: Map<String, String>,
39+
val toolBar: ToolBar,
3740
) : Parcelable {
3841
companion object {
3942
private const val CONFIG_VERSION_KEY = "config_version"
@@ -54,6 +57,11 @@ data class Theme(
5457
null -> LiquidKeyboard()
5558
else -> LiquidKeyboardMapper(node).map()
5659
},
60+
toolBar =
61+
when (val node = root.get<YamlMap>("tool_bar")) {
62+
null -> ToolBar()
63+
else -> ToolBarMapper(node).map()
64+
},
5765
presetKeys =
5866
when (val map = root.get<YamlMap>("preset_keys")) {
5967
null -> emptyMap()
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2015 - 2025 Rime community
3+
* SPDX-License-Identifier: GPL-3.0-or-later
4+
*/
5+
6+
package com.osfans.trime.data.theme.mapper
7+
8+
import com.charleskorn.kaml.YamlMap
9+
import com.charleskorn.kaml.YamlScalar
10+
import com.charleskorn.kaml.yamlMap
11+
import com.osfans.trime.data.theme.model.ToolBar
12+
13+
class ToolBarMapper(node: YamlMap) : Mapper<ToolBar>(node) {
14+
15+
override fun map(): ToolBar = ToolBar(
16+
primaryButton = node.get<YamlMap>("primary_button")?.let(::mapButton),
17+
buttons = getList("buttons")?.map { mapButton(it.yamlMap) } ?: emptyList(),
18+
buttonSpacing = getInt("button_spacing", 18),
19+
buttonFont = getStringList("button_font"),
20+
)
21+
22+
private fun mapButton(node: YamlMap): ToolBar.Button = ToolBar.Button(
23+
background = node.get<YamlMap>("background")?.let { bg ->
24+
ToolBarButtonBackgroundMapper(bg).map()
25+
},
26+
foreground = node.get<YamlMap>("foreground")?.let { fg ->
27+
ToolBarButtonForegroundMapper(fg).map()
28+
},
29+
action = node.get<YamlScalar>("action")?.content ?: "",
30+
)
31+
}
32+
33+
private class ToolBarButtonBackgroundMapper(node: YamlMap) : Mapper<ToolBar.Button.Background>(node) {
34+
override fun map(): ToolBar.Button.Background = ToolBar.Button.Background(
35+
type = getString("type", "rectangle"),
36+
cornerRadius = getFloat("corner_radius", 10f),
37+
bgNormal = getString("bg_normal"),
38+
bgHighlight = getString("bg_highlight"),
39+
verticalInset = getInt("vertical_inset", 4),
40+
horizontalInset = getInt("horizontal_inset"),
41+
)
42+
}
43+
44+
private class ToolBarButtonForegroundMapper(node: YamlMap) : Mapper<ToolBar.Button.Foreground>(node) {
45+
override fun map(): ToolBar.Button.Foreground = ToolBar.Button.Foreground(
46+
style = getString("style"),
47+
optionStyles = getStringList("option_styles"),
48+
bgNormal = getString("bg_normal"),
49+
bgHighlight = getString("bg_highlight"),
50+
fontSize = getFloat("font_size", 18f),
51+
size = getStringList("size").mapNotNull { it.toIntOrNull() },
52+
padding = getInt("padding", 5),
53+
)
54+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2015 - 2025 Rime community
3+
* SPDX-License-Identifier: GPL-3.0-or-later
4+
*/
5+
6+
package com.osfans.trime.data.theme.model
7+
8+
import android.os.Parcelable
9+
import kotlinx.parcelize.Parcelize
10+
11+
@Parcelize
12+
data class ToolBar(
13+
val primaryButton: Button? = null,
14+
val buttons: List<Button> = emptyList(),
15+
val buttonSpacing: Int = 18,
16+
val buttonFont: List<String> = emptyList(),
17+
) : Parcelable {
18+
19+
@Parcelize
20+
data class Button(
21+
val background: Background? = null,
22+
val foreground: Foreground? = null,
23+
val action: String = "",
24+
) : Parcelable {
25+
26+
@Parcelize
27+
data class Background(
28+
val type: String = "rectangle",
29+
val cornerRadius: Float = 10f,
30+
val bgNormal: String = "",
31+
val bgHighlight: String = "",
32+
val verticalInset: Int = 4,
33+
val horizontalInset: Int = 0,
34+
) : Parcelable
35+
36+
@Parcelize
37+
data class Foreground(
38+
val style: String = "",
39+
val optionStyles: List<String> = emptyList(),
40+
val bgNormal: String? = null,
41+
val bgHighlight: String? = null,
42+
val fontSize: Float = 15f,
43+
val size: List<Int> = emptyList(),
44+
val padding: Int = 5,
45+
) : Parcelable
46+
}
47+
}

app/src/main/java/com/osfans/trime/ime/bar/QuickBar.kt

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ import android.view.inputmethod.EditorInfo
1111
import android.widget.ViewAnimator
1212
import androidx.annotation.RequiresApi
1313
import com.osfans.trime.R
14+
import com.osfans.trime.core.RimeMessage
1415
import com.osfans.trime.core.RimeProto
1516
import com.osfans.trime.daemon.RimeSession
1617
import com.osfans.trime.data.prefs.AppPrefs
1718
import com.osfans.trime.data.theme.ColorManager
19+
import com.osfans.trime.data.theme.KeyActionManager
1820
import com.osfans.trime.data.theme.Theme
1921
import com.osfans.trime.ime.bar.ui.AlwaysUi
2022
import com.osfans.trime.ime.bar.ui.CandidateUi
@@ -26,6 +28,7 @@ import com.osfans.trime.ime.candidates.popup.PopupCandidatesMode
2628
import com.osfans.trime.ime.candidates.unrolled.window.FlexboxUnrolledCandidateWindow
2729
import com.osfans.trime.ime.core.TrimeInputMethodService
2830
import com.osfans.trime.ime.dependency.InputScope
31+
import com.osfans.trime.ime.keyboard.CommonKeyboardActionListener
2932
import com.osfans.trime.ime.keyboard.InputFeedbackManager
3033
import com.osfans.trime.ime.keyboard.KeyboardWindow
3134
import com.osfans.trime.ime.option.SwitchOptionWindow
@@ -46,9 +49,12 @@ class QuickBar(
4649
private val theme: Theme,
4750
private val windowManager: BoardWindowManager,
4851
lazyCandidate: Lazy<CandidateModule>,
52+
lazyCommonKeyboardActionListener: Lazy<CommonKeyboardActionListener>,
4953
) : InputBroadcastReceiver {
5054
private val candidate by lazyCandidate
5155

56+
private val commonKeyboardActionListener by lazyCommonKeyboardActionListener
57+
5258
private val prefs = AppPrefs.defaultInstance()
5359

5460
private val hideQuickBar by prefs.keyboard.hideQuickBar
@@ -59,22 +65,18 @@ class QuickBar(
5965
private fun evalAlwaysUiState() {
6066
val newState =
6167
when {
62-
else -> AlwaysUi.State.Empty
68+
else -> AlwaysUi.State.Toolbar
6369
}
6470
if (newState == alwaysUi.currentState) return
6571
alwaysUi.updateState(newState)
6672
}
6773

6874
private val alwaysUi: AlwaysUi by lazy {
69-
AlwaysUi(context, theme).apply {
70-
moreButton.apply {
71-
setOnClickListener {
72-
windowManager.attachWindow(SwitchOptionWindow(context, service, rime, theme))
73-
}
74-
}
75-
hideKeyboardButton.apply {
76-
setOnClickListener { service.requestHideSelf(0) }
77-
}
75+
AlwaysUi(context, theme) { action ->
76+
action?.let { commonKeyboardActionListener.listener.onAction(KeyActionManager.getAction(it)) }
77+
?: windowManager.attachWindow(SwitchOptionWindow(context, service, rime, theme))
78+
}.apply {
79+
hideKeyboardButton.setOnClickListener { service.requestHideSelf(0) }
7880
}
7981
}
8082

@@ -213,4 +215,8 @@ class QuickBar(
213215
QuickBarStateMachine.BooleanKey.SuggestionEmpty to isEmpty,
214216
)
215217
}
218+
219+
override fun onRimeOptionUpdated(value: RimeMessage.OptionMessage.Data) {
220+
alwaysUi.updateButtonsStyle()
221+
}
216222
}

0 commit comments

Comments
 (0)