Skip to content

Commit c7a88e9

Browse files
authored
Merge pull request #1392 from ZalithLauncher/main
merge main
2 parents 43462ec + 3ff878b commit c7a88e9

68 files changed

Lines changed: 1516 additions & 711 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release_ci.yml

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,17 @@ jobs:
3737
3838
find ./artifacts -name "*.apk" -type f -exec cp {} ./apks/ \;
3939
40-
# 检查是否有APK文件
41-
if [ -z "$(ls -A ./apks/)" ]; then
42-
echo "No APK files found to upload!"
43-
exit 1
44-
fi
40+
for dir in ./artifacts/*/; do
41+
dir_name=$(basename "$dir")
4542
46-
# 处理映射文件 区分架构
47-
for mapping_dir in ./artifacts/mapping\ \(*\)/; do
48-
if [ -d "$mapping_dir" ] && [ -f "${mapping_dir}mapping.txt" ]; then
49-
dirname=$(basename "$mapping_dir")
50-
cp "${mapping_dir}mapping.txt" "./mappings/${dirname}.txt"
51-
echo "Copied: ${dirname}.txt"
43+
# 跳过 APK
44+
if [[ "$dir_name" == *"release apk"* ]]; then
45+
continue
46+
fi
47+
48+
if [[ "$dir_name" == mapping* ]]; then
49+
(cd "$dir" && zip -r "../${dir_name}.zip" .)
50+
mv "./artifacts/${dir_name}.zip" ./mappings/
5251
fi
5352
done
5453
@@ -61,5 +60,5 @@ jobs:
6160
tag_name: ${{ github.event.release.tag_name }}
6261
files: |
6362
./apks/*.apk
64-
./mappings/*.txt
63+
./mappings/*.zip
6564
fail_on_unmatched_files: true

LayerController/src/main/java/com/movtery/layer_controller/Layout.kt

Lines changed: 64 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import androidx.compose.runtime.Composable
3131
import androidx.compose.runtime.CompositionLocalProvider
3232
import androidx.compose.runtime.getValue
3333
import androidx.compose.runtime.key
34-
import androidx.compose.runtime.mutableStateMapOf
3534
import androidx.compose.runtime.remember
3635
import androidx.compose.runtime.rememberUpdatedState
3736
import androidx.compose.ui.Alignment
@@ -56,6 +55,8 @@ import com.movtery.layer_controller.observable.ObservableButtonStyle
5655
import com.movtery.layer_controller.observable.ObservableControlLayer
5756
import com.movtery.layer_controller.observable.ObservableControlLayout
5857
import com.movtery.layer_controller.observable.ObservableWidget
58+
import com.movtery.layer_controller.observable.TouchProcessor
59+
import com.movtery.layer_controller.observable.TouchSession
5960
import com.movtery.layer_controller.utils.getWidgetPosition
6061

6162
/**
@@ -143,7 +144,6 @@ private fun BoxWithConstraintsScope.BaseControlBoxLayout(
143144
val reversedLayers = remember(layers) { layers.reversed() }
144145
val styles by observedLayout.styles.collectAsStateWithLifecycle()
145146

146-
val allActiveWidgets = remember { mutableStateMapOf<PointerId, List<ObservableWidget>>() }
147147
val currentCheckOccupiedPointers by rememberUpdatedState(checkOccupiedPointers)
148148
val currentIsCursorGrabbing by rememberUpdatedState(isCursorGrabbing)
149149
val currentHideLayerWhen by rememberUpdatedState(hideLayerWhen)
@@ -158,6 +158,14 @@ private fun BoxWithConstraintsScope.BaseControlBoxLayout(
158158
}
159159
}
160160

161+
//触控管线
162+
val touchSession = remember { TouchSession() }
163+
val touchProcessor = remember(screenSize) {
164+
TouchProcessor(eventHandler) { widget ->
165+
getWidgetPosition(widget, widget.internalRenderSize, screenSize)
166+
}
167+
}
168+
161169
Box(
162170
modifier = modifier
163171
.pointerInput(reversedLayers, hideLayerWhen) {
@@ -166,140 +174,36 @@ private fun BoxWithConstraintsScope.BaseControlBoxLayout(
166174
val event = awaitPointerEvent(pass = PointerEventPass.Initial)
167175

168176
event.changes.forEach { change ->
169-
val position = change.position
170177
val pointerId = change.id
171-
val isPressed = change.pressed
172-
173-
//抬起时,总是尝试释放该指针下活跃的按钮
174-
//避免子级占用了指针后,导致按钮状态无法被释放
175-
if (!isPressed) {
176-
allActiveWidgets.remove(pointerId)?.forEach { widget ->
178+
//手指抬起,清理该指针所有状态
179+
if (!change.pressed) {
180+
touchSession.endPointer(pointerId).forEach { widget ->
181+
//释放该指针事件
177182
widget.onReleaseEvent(eventHandler, reversedLayers)
178183
}
179184
return@forEach
180185
}
181186

182-
if (
183-
change.isConsumed ||
184-
//不处理被子级占用的指针
185-
currentCheckOccupiedPointers(pointerId)
186-
) {
187-
return@forEach
187+
if (change.isConsumed || currentCheckOccupiedPointers(pointerId)) {
188+
return@forEach //跳过已消费或被占用的指针
188189
}
189190

190-
//在可见控件层中,收集所有可见的按钮
191-
val visibleWidgets: List<ObservableWidget> = layers //使用原始控件层顺序,保证触摸逻辑正常
192-
.filter { layer ->
193-
checkLayerVisibility(
194-
layer = layer,
195-
hideLayerWhen = currentHideLayerWhen,
196-
isUsingJoystick = isUsingJoystick,
197-
isCursorGrabbing = currentIsCursorGrabbing,
198-
visibilityType = layer.visibilityType
199-
)
200-
}
201-
.flatMap { layer ->
202-
//顶向下的顺序影响控件层的处理优先级
203-
layer.normalButtons.value.reversed()
204-
}
205-
206-
//查找当前指针在哪些按钮上
207-
val targetWidgets = visibleWidgets
208-
.filter { widget ->
209-
if (!widget.canTouch()) return@filter false
210-
211-
if (!checkVisibility(currentIsCursorGrabbing, widget.onCheckVisibilityType())) {
212-
//隐藏了,不响应事件
213-
return@filter false
214-
}
215-
216-
val size = widget.internalRenderSize
217-
val offset = getWidgetPosition(widget, size, screenSize)
218-
219-
val x = position.x
220-
val y = position.y
221-
x >= offset.x && x <= offset.x + size.width &&
222-
y >= offset.y && y <= offset.y + size.height
223-
}.let { list ->
224-
if (list.isEmpty()) return@let list
225-
226-
val firstDeepWidget = list.firstOrNull { it.supportsDeepTouchDetection() }
227-
228-
when {
229-
firstDeepWidget == null -> list
230-
else -> {
231-
val topIndex = list.indexOf(firstDeepWidget)
232-
list.subList(0, topIndex + 1).filter { widget ->
233-
!widget.canProcess()
234-
}
235-
}
236-
}
237-
}
238-
239-
var activeWidgets = allActiveWidgets[pointerId] ?: emptyList()
240-
241-
//检查是否移出边界
242-
if (activeWidgets.isNotEmpty()) {
243-
val backInBounds = mutableListOf<ObservableWidget>()
244-
val releasedWidgets = mutableListOf<ObservableWidget>()
245-
for (widget in activeWidgets) {
246-
//检查组件是否可以响应移除边界即松开
247-
if (!widget.isReleaseOnOutOfBounds()) continue
248-
249-
val size = widget.internalRenderSize
250-
val offset = getWidgetPosition(widget, size, screenSize)
251-
val isOutOfBounds = position.x !in offset.x..(offset.x + size.width) ||
252-
position.y !in offset.y..(offset.y + size.height)
253-
254-
if (isOutOfBounds) {
255-
widget.onReleaseEvent(eventHandler, reversedLayers)
256-
releasedWidgets.add(widget)
257-
} else {
258-
backInBounds.add(widget)
259-
}
260-
}
261-
//fix: 移出边界时应移出组,否则滑动回已释放的按钮无法再次触发
262-
if (releasedWidgets.isNotEmpty()) {
263-
activeWidgets = activeWidgets - releasedWidgets
264-
allActiveWidgets[pointerId] = activeWidgets
265-
}
266-
//fix: 应该在抬起事件全部处理完成后再处理 #941
267-
if (backInBounds.isNotEmpty()) {
268-
for (widget in backInBounds) {
269-
widget.onPointerBackInBounds(eventHandler, reversedLayers)
270-
}
271-
}
272-
}
273-
274-
when {
275-
targetWidgets.isEmpty() -> {}
276-
else -> {
277-
for (targetWidget in targetWidgets) {
278-
if (targetWidget.canProcess()) {
279-
return@forEach //拒绝处理该事件
280-
}
281-
282-
targetWidget.onTouchEvent(
283-
eventHandler = eventHandler,
284-
allLayers = reversedLayers,
285-
change = change,
286-
activeWidgets = activeWidgets,
287-
addThis = {
288-
allActiveWidgets[pointerId] = activeWidgets + listOf(targetWidget)
289-
},
290-
consumeEvent = { value ->
291-
if (value) {
292-
change.consume()
293-
} else {
294-
//将指针标记为仅接受滑动处理
295-
//期望子级不对点击事件等进行处理
296-
markPointerAsMoveOnly(pointerId)
297-
}
298-
}
299-
)
300-
}
301-
}
302-
}
191+
//收集可见控件
192+
val visibleWidgets = collectVisibleWidgets(
193+
layers = layers,
194+
hideLayerWhen = currentHideLayerWhen,
195+
isUsingJoystick = isUsingJoystick,
196+
isCursorGrabbing = currentIsCursorGrabbing,
197+
)
198+
199+
touchProcessor.processFrame(
200+
session = touchSession,
201+
change = change,
202+
visibleWidgets = visibleWidgets,
203+
allLayers = reversedLayers,
204+
consumeEvent = { it.consume() },
205+
markPointerAsMoveOnly = markPointerAsMoveOnly,
206+
)
303207
}
304208
}
305209
}
@@ -421,6 +325,38 @@ private fun ControlsRendererLayer(
421325
}
422326
}
423327

328+
/**
329+
* 收集所有可见控件层中的可触控控件
330+
* 只做图层可见性、控件可见类型检查,命中和深度检测由 [TouchProcessor] 提供
331+
*/
332+
private fun collectVisibleWidgets(
333+
layers: List<ObservableControlLayer>,
334+
hideLayerWhen: HideLayerWhen,
335+
isUsingJoystick: Boolean,
336+
isCursorGrabbing: Boolean,
337+
): List<ObservableWidget> {
338+
return layers
339+
.filter { layer ->
340+
checkLayerVisibility(
341+
layer = layer,
342+
hideLayerWhen = hideLayerWhen,
343+
isUsingJoystick = isUsingJoystick,
344+
isCursorGrabbing = isCursorGrabbing,
345+
visibilityType = layer.visibilityType,
346+
)
347+
}
348+
.flatMap { layer ->
349+
//反转,从顶到底
350+
layer.normalButtons.value.reversed()
351+
}
352+
.filter { widget ->
353+
widget.canTouch() && checkVisibility(
354+
isCursorGrabbing = isCursorGrabbing,
355+
visibilityType = widget.onCheckVisibilityType()
356+
)
357+
}
358+
}
359+
424360
private fun checkLayerVisibility(
425361
layer: ObservableControlLayer,
426362
hideLayerWhen: HideLayerWhen,

LayerController/src/main/java/com/movtery/layer_controller/layout/Buttons.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ import androidx.compose.ui.unit.dp
3737
import androidx.compose.ui.unit.sp
3838
import com.movtery.layer_controller.data.TextAlignment
3939
import com.movtery.layer_controller.event.EventHandler
40-
import com.movtery.layer_controller.observable.*
40+
import com.movtery.layer_controller.observable.DefaultObservableButtonStyle
41+
import com.movtery.layer_controller.observable.ObservableButtonStyle
42+
import com.movtery.layer_controller.observable.ObservableNormalData
43+
import com.movtery.layer_controller.observable.ObservableTextData
44+
import com.movtery.layer_controller.observable.ObservableTranslatableString
45+
import com.movtery.layer_controller.observable.ObservableWidget
4146
import com.movtery.layer_controller.utils.buttonContentColorAsState
4247
import com.movtery.layer_controller.utils.buttonFontSizeAsState
4348
import com.movtery.layer_controller.utils.buttonSize
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Zalith Launcher 2
3+
* Copyright (C) 2025 MovTery <movtery228@qq.com> and contributors
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13+
* See the GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
17+
*/
18+
19+
package com.movtery.layer_controller.observable
20+
21+
/**
22+
* 控件的交互行为模型
23+
*/
24+
sealed class InteractionBehavior {
25+
/**
26+
* 手指移出控件边界时是否自动释放
27+
*/
28+
abstract val releaseOnOutOfBounds: Boolean
29+
30+
/**
31+
* 是否允许其他控件的指针滑动进入本控件
32+
*/
33+
abstract val canBeSwipedTo: Boolean
34+
35+
/**
36+
* 当本控件处于活跃状态时,是否阻止滑动链向其他控件传播
37+
*/
38+
abstract val blocksSwipeChain: Boolean
39+
40+
41+
42+
/**
43+
* 普通按钮
44+
* 按下保持,松开释放,不参与滑动联动
45+
*/
46+
data object Press : InteractionBehavior() {
47+
override val releaseOnOutOfBounds: Boolean get() = false
48+
override val canBeSwipedTo: Boolean get() = false
49+
override val blocksSwipeChain: Boolean get() = false
50+
}
51+
52+
/**
53+
* 可滑动按钮
54+
* 移出边界自动释放,支持滑动联动
55+
*/
56+
data object Swipable : InteractionBehavior() {
57+
override val releaseOnOutOfBounds: Boolean get() = true
58+
override val canBeSwipedTo: Boolean get() = true
59+
override val blocksSwipeChain: Boolean get() = false
60+
}
61+
62+
/**
63+
* 可切换按钮:
64+
* 点击切换开/关,不可滑动联动,活跃时阻止滑动链传播
65+
*/
66+
data object Toggle : InteractionBehavior() {
67+
override val releaseOnOutOfBounds: Boolean get() = false
68+
override val canBeSwipedTo: Boolean get() = false
69+
override val blocksSwipeChain: Boolean get() = true
70+
}
71+
72+
companion object {
73+
fun from(isSwipple: Boolean, isToggleable: Boolean): InteractionBehavior = when {
74+
isToggleable -> Toggle
75+
isSwipple -> Swipable
76+
else -> Press
77+
}
78+
}
79+
}

0 commit comments

Comments
 (0)