Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d58b5772df | ||
|
|
fff75700ac | ||
|
|
ed5d9a9c78 | ||
|
|
03964ca313 |
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
|
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.9.24"
|
kotlin("jvm") version "1.9.24"
|
||||||
@@ -6,7 +6,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "com.tpuahsiew.wirc"
|
group = "com.tpuahsiew.wirc"
|
||||||
version = "0.1.15"
|
version = "0.1.17"
|
||||||
|
|
||||||
// No project-level repositories{} block here: settings.gradle.kts sets
|
// No project-level repositories{} block here: settings.gradle.kts sets
|
||||||
// dependencyResolutionManagement.repositoriesMode = FAIL_ON_PROJECT_REPOS and already
|
// dependencyResolutionManagement.repositoriesMode = FAIL_ON_PROJECT_REPOS and already
|
||||||
@@ -40,7 +40,7 @@ compose.desktop {
|
|||||||
appResourcesRootDir.set(project.file("installerResources"))
|
appResourcesRootDir.set(project.file("installerResources"))
|
||||||
targetFormats(TargetFormat.Msi, TargetFormat.Exe)
|
targetFormats(TargetFormat.Msi, TargetFormat.Exe)
|
||||||
packageName = "wIRC"
|
packageName = "wIRC"
|
||||||
packageVersion = "0.1.15"
|
packageVersion = "0.1.17"
|
||||||
windows {
|
windows {
|
||||||
menuGroup = "wIRC"
|
menuGroup = "wIRC"
|
||||||
upgradeUuid = "8f14e45f-ceea-4a5e-9c3b-5b1f0e8c2b7a"
|
upgradeUuid = "8f14e45f-ceea-4a5e-9c3b-5b1f0e8c2b7a"
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@file:OptIn(androidx.compose.ui.text.ExperimentalTextApi::class)
|
||||||
|
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
@@ -816,7 +818,7 @@ fun MessageScrollback(
|
|||||||
.first { item ->
|
.first { item ->
|
||||||
item == null || (item.index < capturedList.size && item.key == capturedList[item.index].seq)
|
item == null || (item.index < capturedList.size && item.key == capturedList[item.index].seq)
|
||||||
}
|
}
|
||||||
listState.scrollToItem(0)
|
if (stickToBottom) listState.scrollToItem(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -847,14 +849,30 @@ fun MessageScrollback(
|
|||||||
// then converts that now-stale pixel value using the NEW density, so the
|
// then converts that now-stale pixel value using the NEW density, so the
|
||||||
// two silently disagreed until the window (and this remember scope) was
|
// two silently disagreed until the window (and this remember scope) was
|
||||||
// recreated by closing and reopening it.
|
// recreated by closing and reopening it.
|
||||||
val lineHeightPx = remember(displaySettings.fontSize, displaySettings.fontId, density) {
|
// The buffer text style gets extra vertical air on top of the font's own natural
|
||||||
textMeasurer.measure(
|
// line height (every bundled font has different metrics, so a fixed multiplier
|
||||||
text = "Mg",
|
// would loosen some fonts and tighten others). The same style is used to measure
|
||||||
style = androidx.compose.ui.text.TextStyle(
|
// the line height below AND to draw every row, so the whole-line snapping stays
|
||||||
fontFamily = AppFonts.familyFor(displaySettings.fontId),
|
// exactly in step with what is on screen. LineHeightStyle with Trim.None and a
|
||||||
fontSize = displaySettings.fontSize.sp
|
// centered alignment makes each line exactly lineHeight tall, with the extra
|
||||||
|
// space split above and below the text.
|
||||||
|
val bufferTextStyle = remember(displaySettings.fontSize, displaySettings.fontId, density) {
|
||||||
|
val base = androidx.compose.ui.text.TextStyle(
|
||||||
|
fontFamily = AppFonts.familyFor(displaySettings.fontId),
|
||||||
|
fontSize = displaySettings.fontSize.sp
|
||||||
|
)
|
||||||
|
val naturalPx = textMeasurer.measure(text = "Mg", style = base).size.height
|
||||||
|
val spacedLine = with(density) { (naturalPx + (displaySettings.fontSize * LINE_SPACING_EXTRA).sp.toPx()).toSp() }
|
||||||
|
base.copy(
|
||||||
|
lineHeight = spacedLine,
|
||||||
|
lineHeightStyle = androidx.compose.ui.text.style.LineHeightStyle(
|
||||||
|
alignment = androidx.compose.ui.text.style.LineHeightStyle.Alignment.Center,
|
||||||
|
trim = androidx.compose.ui.text.style.LineHeightStyle.Trim.None
|
||||||
)
|
)
|
||||||
).size.height
|
)
|
||||||
|
}
|
||||||
|
val lineHeightPx = remember(bufferTextStyle, density) {
|
||||||
|
textMeasurer.measure(text = "Mg", style = bufferTextStyle).size.height
|
||||||
}
|
}
|
||||||
val lineHeightDp = with(density) { lineHeightPx.toDp() }
|
val lineHeightDp = with(density) { lineHeightPx.toDp() }
|
||||||
// Ruled out (see task: newest-message-missing investigation) as the
|
// Ruled out (see task: newest-message-missing investigation) as the
|
||||||
@@ -885,10 +903,7 @@ fun MessageScrollback(
|
|||||||
// offset correction through stickToBottom's own effect instead of a
|
// offset correction through stickToBottom's own effect instead of a
|
||||||
// second independent one.
|
// second independent one.
|
||||||
|
|
||||||
val textStyle = androidx.compose.ui.text.TextStyle(
|
val textStyle = bufferTextStyle
|
||||||
fontFamily = AppFonts.familyFor(displaySettings.fontId),
|
|
||||||
fontSize = displaySettings.fontSize.sp
|
|
||||||
)
|
|
||||||
val selection = selectionState.selection
|
val selection = selectionState.selection
|
||||||
|
|
||||||
Column(modifier = Modifier.fillMaxSize()) {
|
Column(modifier = Modifier.fillMaxSize()) {
|
||||||
@@ -908,6 +923,31 @@ fun MessageScrollback(
|
|||||||
.height(snappedHeight)
|
.height(snappedHeight)
|
||||||
.onGloballyPositioned { containerCoords[0] = it }
|
.onGloballyPositioned { containerCoords[0] = it }
|
||||||
.pointerHoverIcon(PointerIcon.Text)
|
.pointerHoverIcon(PointerIcon.Text)
|
||||||
|
// Wheel-up means the user is reading backscroll: stop sticking to the bottom RIGHT NOW,
|
||||||
|
// on the raw wheel event. The scroll-state effect up top can miss a wheel scroll that starts
|
||||||
|
// and finishes inside one frame (isScrollInProgress is never seen as true), which left
|
||||||
|
// stickToBottom set and let the next incoming line yank the buffer back down.
|
||||||
|
// Wheel-down re-checks the resting position shortly after and re-enables sticking if the
|
||||||
|
// user is back at the bottom.
|
||||||
|
.pointerInput(Unit) {
|
||||||
|
awaitPointerEventScope {
|
||||||
|
while (true) {
|
||||||
|
val event = awaitPointerEvent(PointerEventPass.Initial)
|
||||||
|
if (event.type == PointerEventType.Scroll) {
|
||||||
|
val dy = event.changes.firstOrNull()?.scrollDelta?.y ?: 0f
|
||||||
|
if (dy < 0f) {
|
||||||
|
stickToBottom = false
|
||||||
|
} else if (dy > 0f) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
delay(200)
|
||||||
|
stickToBottom = listState.firstVisibleItemIndex == 0 &&
|
||||||
|
listState.firstVisibleItemScrollOffset <= stickToBottomThresholdPx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Remembers the row under a right-click for the menu's "Copy line". Watches the
|
// Remembers the row under a right-click for the menu's "Copy line". Watches the
|
||||||
// Initial pass, because ContextMenuArea takes the right-press there, before
|
// Initial pass, because ContextMenuArea takes the right-press there, before
|
||||||
// the handler below would see it.
|
// the handler below would see it.
|
||||||
@@ -1114,6 +1154,11 @@ fun MessageScrollback(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extra vertical space added to every buffer line, as a fraction of the font size, on top of
|
||||||
|
// the font's natural line height. 0.30 is a modest loosening; raise it for more air between
|
||||||
|
// lines (0.45 is roomy), lower it for less. Applies to every font in the font list.
|
||||||
|
private const val LINE_SPACING_EXTRA = 0.30f
|
||||||
|
|
||||||
// Reports this element's height as a whole number of text lines ([linePx] each): rounded up
|
// Reports this element's height as a whole number of text lines ([linePx] each): rounded up
|
||||||
// for image slots (the picture is never cut), to the nearest line for text rows (a wrapped
|
// for image slots (the picture is never cut), to the nearest line for text rows (a wrapped
|
||||||
// message can measure a pixel or two off an exact multiple). Content is drawn from the top.
|
// message can measure a pixel or two off an exact multiple). Content is drawn from the top.
|
||||||
|
|||||||
Reference in New Issue
Block a user