Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
242d7a7c42 |
+2
-2
@@ -6,7 +6,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = "com.tpuahsiew.wirc"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
|
||||
// No project-level repositories{} block here: settings.gradle.kts sets
|
||||
// dependencyResolutionManagement.repositoriesMode = FAIL_ON_PROJECT_REPOS and already
|
||||
@@ -38,7 +38,7 @@ compose.desktop {
|
||||
appResourcesRootDir.set(project.file("installerResources"))
|
||||
targetFormats(TargetFormat.Msi, TargetFormat.Exe)
|
||||
packageName = "wIRC"
|
||||
packageVersion = "0.1.5"
|
||||
packageVersion = "0.1.6"
|
||||
windows {
|
||||
menuGroup = "wIRC"
|
||||
upgradeUuid = "8f14e45f-ceea-4a5e-9c3b-5b1f0e8c2b7a"
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package ui
|
||||
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.hoverable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsHoveredAsState
|
||||
import androidx.compose.foundation.window.WindowDraggableArea
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
@@ -27,6 +31,7 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -68,11 +73,12 @@ import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlin.math.floor
|
||||
|
||||
// TEMP DEBUG (see task: auto-scroll from-scratch audit) - remove once confirmed fixed.
|
||||
// label identifies which window's log line this is, so multi-window repros are legible.
|
||||
private fun ircUiDebug(label: String, msg: String) {
|
||||
println("[ui-debug][$label] $msg")
|
||||
}
|
||||
// Title-bar button fade (nicklist / settings / minimize / close). The buttons sit at
|
||||
// TITLE_BAR_BUTTON_REST_ALPHA until hovered, then animate to TITLE_BAR_BUTTON_HOVER_ALPHA
|
||||
// over TITLE_BAR_BUTTON_FADE_MS. The channel name text is not affected.
|
||||
private const val TITLE_BAR_BUTTON_REST_ALPHA = 0f
|
||||
private const val TITLE_BAR_BUTTON_HOVER_ALPHA = 1f
|
||||
private const val TITLE_BAR_BUTTON_FADE_MS = 150
|
||||
|
||||
// Single-space separator between the padded nick column and the message body. Previously
|
||||
// a 2-space trailing gap on CHAT lines (on top of the padEnd() column padding itself),
|
||||
@@ -406,6 +412,8 @@ fun WindowScope.ChannelScreen(
|
||||
// because manually accumulating dragAmount into windowState.position fights
|
||||
// with the OS's own window-move handling; WindowDraggableArea talks to that
|
||||
// directly instead of reimplementing it.
|
||||
// The four buttons on the right fade in on hover (see FadingTitleBarButton); the
|
||||
// channel name stays fully opaque.
|
||||
WindowDraggableArea(modifier = Modifier.fillMaxWidth()) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@@ -416,20 +424,20 @@ fun WindowScope.ChannelScreen(
|
||||
) {
|
||||
Text(channelName, color = textColor, fontFamily = FontFamily.Monospace, style = MaterialTheme.typography.labelSmall)
|
||||
Row {
|
||||
IconButton(onClick = { onShowUserListChange(!displaySettings.showUserList) }, modifier = Modifier.size(28.dp)) {
|
||||
FadingTitleBarButton(onClick = { onShowUserListChange(!displaySettings.showUserList) }) {
|
||||
Icon(Icons.Default.Person, contentDescription = "Toggle user list", tint = textColor, modifier = Modifier.size(16.dp))
|
||||
}
|
||||
IconButton(onClick = { showSettings = !showSettings }, modifier = Modifier.size(28.dp)) {
|
||||
FadingTitleBarButton(onClick = { showSettings = !showSettings }) {
|
||||
Icon(Icons.Default.Settings, contentDescription = "Window settings", tint = textColor, modifier = Modifier.size(16.dp))
|
||||
}
|
||||
IconButton(onClick = { windowState.isMinimized = true }, modifier = Modifier.size(28.dp)) {
|
||||
FadingTitleBarButton(onClick = { windowState.isMinimized = true }) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(width = 10.dp, height = 2.dp)
|
||||
.background(textColor)
|
||||
)
|
||||
}
|
||||
IconButton(onClick = onCloseWindow, modifier = Modifier.size(28.dp)) {
|
||||
FadingTitleBarButton(onClick = onCloseWindow) {
|
||||
Icon(Icons.Default.Close, contentDescription = "Close window", tint = textColor, modifier = Modifier.size(16.dp))
|
||||
}
|
||||
}
|
||||
@@ -796,6 +804,31 @@ fun WindowScope.ChannelScreen(
|
||||
}
|
||||
}
|
||||
|
||||
// A 28dp title-bar IconButton that rests invisible and fades in while hovered. The fade
|
||||
// is a graphicsLayer alpha only - the button is always laid out, drawn and hit-tested, so
|
||||
// it still gets hover and clicks at alpha 0. The alpha is read inside the graphicsLayer
|
||||
// lambda so the animation only re-draws the layer instead of recomposing every frame.
|
||||
// IconButton's own hover/press state layer is drawn inside this layer, so it still shows
|
||||
// once the button has faded in.
|
||||
@Composable
|
||||
private fun FadingTitleBarButton(onClick: () -> Unit, content: @Composable () -> Unit) {
|
||||
val hoverSource = remember { MutableInteractionSource() }
|
||||
val hovered by hoverSource.collectIsHoveredAsState()
|
||||
val alpha by animateFloatAsState(
|
||||
targetValue = if (hovered) TITLE_BAR_BUTTON_HOVER_ALPHA else TITLE_BAR_BUTTON_REST_ALPHA,
|
||||
animationSpec = tween(durationMillis = TITLE_BAR_BUTTON_FADE_MS)
|
||||
)
|
||||
IconButton(
|
||||
onClick = onClick,
|
||||
modifier = Modifier
|
||||
.size(28.dp)
|
||||
.hoverable(hoverSource)
|
||||
.graphicsLayer { this.alpha = alpha }
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
// In-memory LRU cache of decoded image bitmaps, keyed by URL, so scrolling a channel
|
||||
// with image previews on doesn't re-fetch/re-decode images that already loaded.
|
||||
private object ImageCache {
|
||||
|
||||
Reference in New Issue
Block a user