Skip to content

Commit e755574

Browse files
committed
fix image view crash and close welcome view by default
1 parent f8e5a06 commit e755574

5 files changed

Lines changed: 121 additions & 86 deletions

File tree

composeApp/src/commonMain/kotlin/me/lkl/dalvikus/App.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ internal val dalvikusSettings: DalvikusSettings by lazy {
128128
}
129129
internal val tabManager: TabManager by lazy {
130130
TabManager(
131-
initialTabs = listOf(WelcomeTab())
131+
initialTabs = listOf()
132132
)
133133
}
134134

composeApp/src/commonMain/kotlin/me/lkl/dalvikus/tree/archive/ZipEntryImageNode.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import brut.androlib.res.decoder.AndroidManifestResourceParser
66
import brut.androlib.res.decoder.ResStreamDecoder
77
import brut.androlib.res.decoder.ResXmlPullStreamDecoder
88
import me.lkl.dalvikus.tabs.CodeTab
9+
import me.lkl.dalvikus.tabs.ImageTab
910
import me.lkl.dalvikus.tabs.TabElement
1011
import me.lkl.dalvikus.tree.ContainerNode
1112
import me.lkl.dalvikus.util.guessIfEditableTextually
@@ -25,10 +26,9 @@ class ZipEntryImageNode(
2526
override fun isEditable(): Boolean = false
2627

2728
override suspend fun createTab(): TabElement {
28-
return CodeTab(
29+
return ImageTab(
2930
tabName = name,
3031
tabId = fullPath,
31-
tabIcon = icon,
3232
contentProvider = this
3333
)
3434
}

composeApp/src/commonMain/kotlin/me/lkl/dalvikus/ui/image/ImageView.kt

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package me.lkl.dalvikus.ui.image
22

33
import androidx.compose.foundation.Image
44
import androidx.compose.foundation.background
5-
import androidx.compose.foundation.gestures.detectTransformGestures
65
import androidx.compose.foundation.layout.Box
76
import androidx.compose.foundation.layout.fillMaxSize
87
import androidx.compose.foundation.layout.padding
@@ -14,9 +13,12 @@ import androidx.compose.runtime.*
1413
import androidx.compose.ui.Alignment
1514
import androidx.compose.ui.Modifier
1615
import androidx.compose.ui.geometry.Offset
16+
import androidx.compose.ui.graphics.FilterQuality
1717
import androidx.compose.ui.graphics.ImageBitmap
1818
import androidx.compose.ui.graphics.graphicsLayer
1919
import androidx.compose.ui.graphics.toComposeImageBitmap
20+
import androidx.compose.ui.input.mouse.mouseScrollFilter
21+
import androidx.compose.ui.input.pointer.PointerEventType
2022
import androidx.compose.ui.input.pointer.pointerInput
2123
import androidx.compose.ui.unit.dp
2224
import androidx.compose.ui.unit.sp
@@ -47,16 +49,31 @@ fun ImageView(tab: ImageTab) {
4749
}
4850

4951
val gestureModifier = Modifier.pointerInput(Unit) {
50-
detectTransformGestures { _, pan, zoom, _ ->
51-
scale = (scale * zoom).coerceIn(0.1f, 10f)
52-
offset += pan
52+
awaitPointerEventScope {
53+
while (true) {
54+
val event = awaitPointerEvent()
55+
if (event.type == PointerEventType.Scroll) {
56+
val scrollDelta = event.changes.first().scrollDelta
57+
val zoomFactor1 = 1.1f
58+
scale = if (scrollDelta.y > 0) {
59+
(scale * zoomFactor1).coerceIn(0.1f, 10f)
60+
} else {
61+
(scale / zoomFactor1).coerceIn(0.1f, 10f)
62+
}
63+
if (true) {
64+
event.changes.first().consume()
65+
}
66+
}
67+
}
5368
}
5469
}
5570

56-
val colors = MaterialTheme.colorScheme
57-
5871
Box(
59-
modifier = Modifier.fillMaxSize().background(colors.background)
72+
modifier = Modifier
73+
.fillMaxSize()
74+
.background(MaterialTheme.colorScheme.surfaceContainerLow)
75+
.padding(24.dp),
76+
contentAlignment = Alignment.Center
6077
) {
6178
image?.let { img ->
6279
val imageWidth = img.width
@@ -74,27 +91,26 @@ fun ImageView(tab: ImageTab) {
7491
) {
7592
Image(
7693
bitmap = img,
94+
filterQuality = FilterQuality.None,
7795
contentDescription = null,
7896
modifier = Modifier
7997
.align(Alignment.Center)
80-
.background(colors.surfaceVariant)
98+
.background(MaterialTheme.colorScheme.surfaceVariant)
8199
)
82100
}
83101

84102
Text(
85103
text = "$imageWidth x $imageHeight",
86-
color = colors.onBackground,
104+
color = MaterialTheme.colorScheme.onBackground,
87105
fontSize = 14.sp,
88106
modifier = Modifier
89107
.align(Alignment.BottomEnd)
90108
.padding(8.dp)
91-
.background(colors.surface.copy(alpha = 0.7f), shape = RoundedCornerShape(4.dp))
92-
.padding(horizontal = 8.dp, vertical = 4.dp)
93109
)
94110
} ?: run {
95111
CircularProgressIndicator(
96112
modifier = Modifier.align(Alignment.Center),
97-
color = colors.primary
113+
color = MaterialTheme.colorScheme.primary
98114
)
99115
}
100116
}

composeApp/src/commonMain/kotlin/me/lkl/dalvikus/ui/tabs/TabManager.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ import androidx.compose.runtime.mutableStateListOf
55
import androidx.compose.runtime.mutableStateOf
66
import androidx.compose.runtime.setValue
77
import me.lkl.dalvikus.tabs.TabElement
8+
import me.lkl.dalvikus.tabs.WelcomeTab
89

910
class TabManager(initialTabs: List<TabElement>) {
1011
private val _tabs = mutableStateListOf<TabElement>().apply { addAll(initialTabs) }
1112
val tabs: List<TabElement> get() = _tabs
1213
val currentTab: TabElement
13-
get() = _tabs.getOrNull(selectedTabIndex) ?: _tabs.firstOrNull()
14-
?: throw IllegalStateException("No tabs available")
14+
get() = _tabs.getOrNull(selectedTabIndex) ?: WelcomeTab()
1515

1616
var selectedTabIndex by mutableStateOf(0)
1717

@@ -23,7 +23,7 @@ class TabManager(initialTabs: List<TabElement>) {
2323

2424
fun closeTab(tab: TabElement) {
2525
val index = _tabs.indexOf(tab)
26-
if (index != -1 && _tabs.size > 1) {
26+
if (index != -1) {
2727
_tabs.removeAt(index)
2828

2929
if (selectedTabIndex >= _tabs.size) {

composeApp/src/commonMain/kotlin/me/lkl/dalvikus/ui/tabs/TabView.kt

Lines changed: 87 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ import androidx.compose.ui.unit.dp
1515
import dalvikus.composeapp.generated.resources.*
1616
import me.lkl.dalvikus.tabManager
1717
import me.lkl.dalvikus.tabs.TabElement
18+
import me.lkl.dalvikus.tabs.WelcomeTab
1819
import org.jetbrains.compose.resources.stringResource
1920

20-
@OptIn(ExperimentalMaterial3Api::class)
2121
@Composable
2222
fun TabView(
2323
) {
@@ -27,82 +27,101 @@ fun TabView(
2727
UnsavedChangesDialog(tabManager, showCloseDialog, pendingCloseTab)
2828
}
2929
Column {
30-
if (tabManager.tabs.isNotEmpty()) {
31-
SecondaryScrollableTabRow(
32-
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
33-
selectedTabIndex = tabManager.selectedTabIndex,
34-
modifier = Modifier.fillMaxWidth(),
35-
edgePadding = 0.dp,
36-
) {
37-
tabManager.tabs.forEachIndexed { index, tab ->
38-
val tooltipState = rememberTooltipState(isPersistent = false)
39-
Tab(
40-
selected = tabManager.selectedTabIndex == index,
41-
onClick = { tabManager.selectTab(index) },
42-
text = {
43-
var unsaved by tab.hasUnsavedChanges
44-
Row(
45-
verticalAlignment = Alignment.CenterVertically,
46-
modifier = Modifier.padding(end = 8.dp)
47-
) {
48-
Icon(
49-
imageVector = tab.tabIcon,
50-
contentDescription = null,
51-
modifier = Modifier.size(16.dp)
52-
)
53-
Spacer(modifier = Modifier.width(4.dp))
54-
TooltipBox(
55-
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(8.dp),
56-
tooltip = {
57-
RichTooltip(
58-
title = { Text(stringResource(Res.string.tooltip_tab_title)) }
59-
) {
60-
Text(
61-
tab.contentProvider.getSourcePath()
62-
?: stringResource(Res.string.unknown_source),
63-
)
64-
}
65-
},
66-
state = tooltipState
67-
) {
68-
Text(
69-
if (unsaved) "${tab.tabName()}*" else tab.tabName(),
70-
maxLines = 1,
71-
overflow = TextOverflow.Ellipsis,
72-
style = MaterialTheme.typography.bodyLarge
73-
)
30+
SecondaryScrollableTabRow(
31+
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
32+
selectedTabIndex = tabManager.selectedTabIndex,
33+
modifier = Modifier.fillMaxWidth(),
34+
edgePadding = 0.dp,
35+
) {
36+
if (tabManager.tabs.isEmpty()) {
37+
Tab(
38+
selected = true,
39+
onClick = { /* no-op */ },
40+
text = {
41+
TabViewContent(
42+
tab = WelcomeTab(),
43+
onClose = null
44+
)
45+
},
46+
)
47+
}
48+
tabManager.tabs.forEachIndexed { index, tab ->
49+
Tab(
50+
selected = tabManager.selectedTabIndex == index,
51+
onClick = { tabManager.selectTab(index) },
52+
text = {
53+
TabViewContent(
54+
tab = tab,
55+
onClose = {
56+
if (tab.hasUnsavedChanges.value) {
57+
pendingCloseTab.value = tab
58+
showCloseDialog.value = true
59+
} else {
60+
tabManager.closeTab(tab)
7461
}
62+
},
63+
)
64+
},
65+
)
66+
}
67+
}
7568

76-
Spacer(modifier = Modifier.width(4.dp))
77-
if (tabManager.tabs.size > 1) {
78-
Icon(
79-
imageVector = Icons.Default.Close,
80-
contentDescription = null,
81-
modifier = Modifier
82-
.size(16.dp)
83-
.clickable {
84-
if (tabManager.tabs.size > 1) {
85-
if (unsaved) {
86-
pendingCloseTab.value = tab
87-
showCloseDialog.value = true
88-
} else {
89-
tabManager.closeTab(tab)
90-
}
91-
}
92-
}
93-
)
94-
}
95-
}
96-
}
69+
TabContentRenderer(tab = tabManager.currentTab)
70+
}
71+
}
72+
73+
@OptIn(ExperimentalMaterial3Api::class)
74+
@Composable
75+
fun TabViewContent(tab: TabElement, onClose: (() -> Unit)?) {
76+
var unsaved by tab.hasUnsavedChanges
77+
val tooltipState = rememberTooltipState(isPersistent = false)
78+
79+
Row(
80+
verticalAlignment = Alignment.CenterVertically
81+
) {
82+
Icon(
83+
imageVector = tab.tabIcon,
84+
contentDescription = null,
85+
modifier = Modifier.size(16.dp)
86+
)
87+
Spacer(modifier = Modifier.width(4.dp))
88+
TooltipBox(
89+
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(8.dp),
90+
tooltip = {
91+
RichTooltip(
92+
title = { Text(stringResource(Res.string.tooltip_tab_title)) }
93+
) {
94+
Text(
95+
tab.contentProvider.getSourcePath()
96+
?: stringResource(Res.string.unknown_source),
9797
)
9898
}
99-
}
99+
},
100+
state = tooltipState
101+
) {
102+
Text(
103+
if (unsaved) "${tab.tabName()}*" else tab.tabName(),
104+
maxLines = 1,
105+
overflow = TextOverflow.Ellipsis,
106+
style = MaterialTheme.typography.bodyLarge
107+
)
108+
}
100109

101-
TabContentRenderer(tab = tabManager.currentTab)
110+
Spacer(modifier = Modifier.width(4.dp))
111+
if(onClose != null) {
112+
Icon(
113+
imageVector = Icons.Default.Close,
114+
contentDescription = null,
115+
modifier = Modifier.size(16.dp)
116+
.clickable {
117+
onClose()
118+
}
119+
)
102120
}
103121
}
104122
}
105123

124+
106125
@Composable
107126
fun UnsavedChangesDialog(
108127
tabManager: TabManager,

0 commit comments

Comments
 (0)