mirror of
https://github.com/amir1376/ab-download-manager.git
synced 2025-02-20 11:43:24 +08:00
redesign about page (#321)
This commit is contained in:
parent
a4c0995760
commit
c91cbb0dbb
@ -1,7 +1,6 @@
|
|||||||
package com.abdownloadmanager.desktop.pages.about
|
package com.abdownloadmanager.desktop.pages.about
|
||||||
|
|
||||||
import com.abdownloadmanager.desktop.AppComponent
|
import com.abdownloadmanager.desktop.AppComponent
|
||||||
import com.abdownloadmanager.desktop.ui.Ui
|
|
||||||
import com.abdownloadmanager.desktop.ui.customwindow.CustomWindow
|
import com.abdownloadmanager.desktop.ui.customwindow.CustomWindow
|
||||||
import com.abdownloadmanager.desktop.ui.customwindow.WindowTitle
|
import com.abdownloadmanager.desktop.ui.customwindow.WindowTitle
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@ -11,9 +10,10 @@ import androidx.compose.ui.unit.DpSize
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.WindowPosition
|
import androidx.compose.ui.window.WindowPosition
|
||||||
import androidx.compose.ui.window.rememberWindowState
|
import androidx.compose.ui.window.rememberWindowState
|
||||||
|
import com.abdownloadmanager.desktop.ui.customwindow.WindowIcon
|
||||||
|
import com.abdownloadmanager.desktop.ui.icon.MyIcons
|
||||||
import com.abdownloadmanager.desktop.ui.theme.LocalUiScale
|
import com.abdownloadmanager.desktop.ui.theme.LocalUiScale
|
||||||
import com.abdownloadmanager.resources.Res
|
import com.abdownloadmanager.resources.Res
|
||||||
import com.abdownloadmanager.resources.*
|
|
||||||
import ir.amirab.util.compose.resources.myStringResource
|
import ir.amirab.util.compose.resources.myStringResource
|
||||||
import ir.amirab.util.desktop.screen.applyUiScale
|
import ir.amirab.util.desktop.screen.applyUiScale
|
||||||
|
|
||||||
@ -43,16 +43,18 @@ fun AboutDialog(
|
|||||||
CustomWindow(
|
CustomWindow(
|
||||||
resizable = false,
|
resizable = false,
|
||||||
onRequestToggleMaximize = null,
|
onRequestToggleMaximize = null,
|
||||||
|
alwaysOnTop = false,
|
||||||
|
onRequestMinimize = null,
|
||||||
state = rememberWindowState(
|
state = rememberWindowState(
|
||||||
position = WindowPosition.Aligned(Alignment.Center),
|
position = WindowPosition.Aligned(Alignment.Center),
|
||||||
size = DpSize(400.dp, 350.dp)
|
size = DpSize(600.dp, 310.dp)
|
||||||
.applyUiScale(LocalUiScale.current)
|
.applyUiScale(LocalUiScale.current)
|
||||||
),
|
),
|
||||||
onCloseRequest = onClose
|
onCloseRequest = onClose
|
||||||
) {
|
) {
|
||||||
WindowTitle(myStringResource(Res.string.about))
|
WindowTitle(myStringResource(Res.string.about))
|
||||||
|
WindowIcon(MyIcons.info)
|
||||||
AboutPage(
|
AboutPage(
|
||||||
close = onClose,
|
|
||||||
onRequestShowOpenSourceLibraries = onRequestShowOpenSourceLibraries,
|
onRequestShowOpenSourceLibraries = onRequestShowOpenSourceLibraries,
|
||||||
onRequestShowTranslators = onRequestShowTranslators
|
onRequestShowTranslators = onRequestShowTranslators
|
||||||
)
|
)
|
||||||
|
@ -2,23 +2,22 @@ package com.abdownloadmanager.desktop.pages.about
|
|||||||
|
|
||||||
import androidx.compose.foundation.*
|
import androidx.compose.foundation.*
|
||||||
import com.abdownloadmanager.utils.compose.LocalTextStyle
|
import com.abdownloadmanager.utils.compose.LocalTextStyle
|
||||||
import com.abdownloadmanager.utils.compose.ProvideTextStyle
|
|
||||||
import com.abdownloadmanager.desktop.ui.icon.MyIcons
|
import com.abdownloadmanager.desktop.ui.icon.MyIcons
|
||||||
import com.abdownloadmanager.desktop.ui.theme.myColors
|
import com.abdownloadmanager.desktop.ui.theme.myColors
|
||||||
import com.abdownloadmanager.desktop.ui.theme.myTextSizes
|
import com.abdownloadmanager.desktop.ui.theme.myTextSizes
|
||||||
import com.abdownloadmanager.desktop.ui.widget.ActionButton
|
|
||||||
import com.abdownloadmanager.desktop.ui.widget.Text
|
import com.abdownloadmanager.desktop.ui.widget.Text
|
||||||
import com.abdownloadmanager.desktop.utils.AppInfo
|
import com.abdownloadmanager.desktop.utils.AppInfo
|
||||||
import com.abdownloadmanager.utils.compose.WithContentAlpha
|
import com.abdownloadmanager.utils.compose.WithContentAlpha
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.interaction.collectIsHoveredAsState
|
import androidx.compose.foundation.interaction.collectIsHoveredAsState
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.*
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.input.pointer.PointerIcon
|
import androidx.compose.ui.input.pointer.PointerIcon
|
||||||
import androidx.compose.ui.input.pointer.pointerHoverIcon
|
import androidx.compose.ui.input.pointer.pointerHoverIcon
|
||||||
import androidx.compose.ui.platform.LocalUriHandler
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
@ -26,117 +25,368 @@ import androidx.compose.ui.text.*
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextDecoration
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.abdownloadmanager.desktop.SharedConstants
|
||||||
import com.abdownloadmanager.utils.compose.widget.MyIcon
|
import com.abdownloadmanager.utils.compose.widget.MyIcon
|
||||||
import com.abdownloadmanager.desktop.ui.util.ifThen
|
import com.abdownloadmanager.desktop.ui.util.ifThen
|
||||||
|
import com.abdownloadmanager.desktop.ui.widget.IconActionButton
|
||||||
|
import com.abdownloadmanager.desktop.ui.widget.Tooltip
|
||||||
|
import com.abdownloadmanager.desktop.utils.div
|
||||||
import com.abdownloadmanager.resources.Res
|
import com.abdownloadmanager.resources.Res
|
||||||
|
import ir.amirab.util.UrlUtils
|
||||||
|
import ir.amirab.util.compose.IconSource
|
||||||
|
import ir.amirab.util.compose.StringSource
|
||||||
|
import ir.amirab.util.compose.asStringSource
|
||||||
import ir.amirab.util.compose.resources.myStringResource
|
import ir.amirab.util.compose.resources.myStringResource
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AboutPage(
|
fun AboutPage(
|
||||||
close: () -> Unit,
|
|
||||||
onRequestShowOpenSourceLibraries: () -> Unit,
|
onRequestShowOpenSourceLibraries: () -> Unit,
|
||||||
onRequestShowTranslators: () -> Unit,
|
onRequestShowTranslators: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Column(Modifier.padding(16.dp)) {
|
Box {
|
||||||
|
BackgroundEffects()
|
||||||
RenderAppInfo(
|
RenderAppInfo(
|
||||||
modifier = Modifier
|
modifier = Modifier,
|
||||||
.weight(1f)
|
|
||||||
.verticalScroll(rememberScrollState()),
|
|
||||||
onRequestShowOpenSourceLibraries = onRequestShowOpenSourceLibraries,
|
onRequestShowOpenSourceLibraries = onRequestShowOpenSourceLibraries,
|
||||||
onRequestShowTranslators = onRequestShowTranslators,
|
onRequestShowTranslators = onRequestShowTranslators,
|
||||||
)
|
)
|
||||||
Row(Modifier.fillMaxWidth().wrapContentWidth(Alignment.End)) {
|
}
|
||||||
ActionButton(
|
}
|
||||||
myStringResource(Res.string.close),
|
|
||||||
onClick = close
|
@Composable
|
||||||
|
private fun AppIconAndVersion(
|
||||||
|
modifier: Modifier,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = modifier.padding(
|
||||||
|
horizontal = 24.dp,
|
||||||
|
vertical = 8.dp,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
val shape = RoundedCornerShape(16.dp)
|
||||||
|
Image(
|
||||||
|
MyIcons.appIcon.rememberPainter(),
|
||||||
|
null,
|
||||||
|
Modifier
|
||||||
|
.shadow(12.dp, shape, spotColor = myColors.primary)
|
||||||
|
.clip(shape)
|
||||||
|
.border(
|
||||||
|
1.dp,
|
||||||
|
Brush.linearGradient(
|
||||||
|
listOf(myColors.primary, myColors.secondary)
|
||||||
|
),
|
||||||
|
shape
|
||||||
|
)
|
||||||
|
.background(myColors.surface)
|
||||||
|
.padding(16.dp)
|
||||||
|
.size(52.dp)
|
||||||
|
)
|
||||||
|
Spacer(Modifier.size(16.dp))
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.Start
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
AppInfo.displayName,
|
||||||
|
fontSize = myTextSizes.lg,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(2.dp))
|
||||||
|
WithContentAlpha(0.75f) {
|
||||||
|
Text(
|
||||||
|
myStringResource(
|
||||||
|
Res.string.version_n,
|
||||||
|
Res.string.version_n_createArgs(
|
||||||
|
value = AppInfo.version.toString(),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
fontSize = myTextSizes.base,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun RenderAppInfo(
|
||||||
|
modifier: Modifier,
|
||||||
|
onRequestShowOpenSourceLibraries: () -> Unit,
|
||||||
|
onRequestShowTranslators: () -> Unit,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier.fillMaxSize(),
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
Modifier.width(250.dp),
|
||||||
|
verticalArrangement = Arrangement.SpaceBetween,
|
||||||
|
) {
|
||||||
|
AppIconAndVersion(Modifier.fillMaxWidth())
|
||||||
|
Spacer(Modifier.weight(1f))
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
myStringResource(Res.string.developed_with_love_for_you),
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(8.dp))
|
||||||
|
Spacer(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(myColors.onBackground / 0.05f)
|
||||||
|
.height(1.dp)
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(8.dp))
|
||||||
|
val websiteUrl = SharedConstants.projectWebsite
|
||||||
|
val websiteDisplayName = remember(websiteUrl) {
|
||||||
|
kotlin.runCatching {
|
||||||
|
URL(websiteUrl).host
|
||||||
|
}.getOrNull() ?: websiteUrl
|
||||||
|
}
|
||||||
|
LinkText(
|
||||||
|
text = websiteDisplayName,
|
||||||
|
link = websiteUrl,
|
||||||
|
showExternalIndicator = false,
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(16.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer(
|
||||||
|
Modifier
|
||||||
|
.fillMaxHeight()
|
||||||
|
.width(1.dp)
|
||||||
|
.background(myColors.onBackground.copy(0.15f))
|
||||||
|
)
|
||||||
|
Column(
|
||||||
|
Modifier.weight(1f)
|
||||||
|
) {
|
||||||
|
CreditsSection(
|
||||||
|
modifier = Modifier.fillMaxWidth().weight(1f),
|
||||||
|
onRequestShowOpenSourceLibraries = onRequestShowOpenSourceLibraries,
|
||||||
|
onRequestShowTranslators = onRequestShowTranslators,
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(1.dp).fillMaxWidth().background(myColors.onBackground / 0.15f))
|
||||||
|
SocialAndLinks(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(myColors.surface / 0.5f)
|
||||||
|
.padding(top = 12.dp)
|
||||||
|
.padding(bottom = 16.dp)
|
||||||
|
.wrapContentWidth(),
|
||||||
|
horizontalPadding = 8.dp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RenderAppInfo(
|
private fun SocialAndLinks(
|
||||||
modifier: Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
horizontalPadding: Dp,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
modifier = modifier
|
||||||
|
.padding(
|
||||||
|
horizontal = horizontalPadding,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
SocialSmallButton(
|
||||||
|
MyIcons.earth,
|
||||||
|
Res.string.visit_the_project_website.asStringSource(),
|
||||||
|
onClick = {
|
||||||
|
UrlUtils.openUrl(SharedConstants.projectWebsite)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
SocialSmallButton(
|
||||||
|
MyIcons.openSource,
|
||||||
|
Res.string.view_the_source_code.asStringSource(),
|
||||||
|
onClick = {
|
||||||
|
UrlUtils.openUrl(SharedConstants.projectSourceCode)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
SocialSmallButton(
|
||||||
|
MyIcons.speaker,
|
||||||
|
Res.string.channel.asStringSource(),
|
||||||
|
onClick = {
|
||||||
|
UrlUtils.openUrl(SharedConstants.telegramChannelUrl)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
SocialSmallButton(
|
||||||
|
MyIcons.group,
|
||||||
|
Res.string.group.asStringSource(),
|
||||||
|
onClick = {
|
||||||
|
UrlUtils.openUrl(SharedConstants.telegramGroupUrl)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
SocialSmallButton(
|
||||||
|
MyIcons.language,
|
||||||
|
Res.string.translators_contribute_title.asStringSource(),
|
||||||
|
onClick = {
|
||||||
|
UrlUtils.openUrl(SharedConstants.projectTranslations)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun CreditsSection(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
onRequestShowOpenSourceLibraries: () -> Unit,
|
onRequestShowOpenSourceLibraries: () -> Unit,
|
||||||
onRequestShowTranslators: () -> Unit,
|
onRequestShowTranslators: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Row(
|
Column(
|
||||||
modifier.fillMaxWidth()
|
modifier
|
||||||
.padding(horizontal = 8.dp),
|
.verticalScroll(rememberScrollState())
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.padding(vertical = 8.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
ProvideTextStyle(
|
val itemModifier = Modifier.fillMaxWidth()
|
||||||
TextStyle(fontSize = myTextSizes.base)
|
AboutPageListItemButton(
|
||||||
) {
|
itemModifier,
|
||||||
Column {
|
icon = MyIcons.hearth,
|
||||||
Row(
|
title = Res.string.this_is_a_free_and_open_source_software.asStringSource(),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
description = Res.string.view_the_source_code.asStringSource(),
|
||||||
) {
|
onClick = {
|
||||||
Image(
|
UrlUtils.openUrl(AppInfo.sourceCode)
|
||||||
MyIcons.appIcon.rememberPainter(),
|
|
||||||
null,
|
|
||||||
Modifier
|
|
||||||
.size(48.dp)
|
|
||||||
)
|
|
||||||
Spacer(Modifier.width(16.dp))
|
|
||||||
Column {
|
|
||||||
Text(
|
|
||||||
AppInfo.displayName,
|
|
||||||
fontSize = myTextSizes.xl,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
)
|
|
||||||
Spacer(Modifier.height(2.dp))
|
|
||||||
WithContentAlpha(0.75f) {
|
|
||||||
Text(
|
|
||||||
myStringResource(
|
|
||||||
Res.string.version_n,
|
|
||||||
Res.string.version_n_createArgs(
|
|
||||||
value = AppInfo.version.toString()
|
|
||||||
)
|
|
||||||
), fontSize = myTextSizes.base
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Spacer(Modifier.height(16.dp))
|
|
||||||
WithContentAlpha(1f) {
|
|
||||||
Text(myStringResource(Res.string.developed_with_love_for_you))
|
|
||||||
LinkText(myStringResource(Res.string.visit_the_project_website), AppInfo.website)
|
|
||||||
|
|
||||||
Spacer(Modifier.height(8.dp))
|
|
||||||
|
|
||||||
Text(myStringResource(Res.string.this_is_a_free_and_open_source_software))
|
|
||||||
LinkText(myStringResource(Res.string.view_the_source_code), AppInfo.sourceCode)
|
|
||||||
Spacer(Modifier.height(8.dp))
|
|
||||||
Text(myStringResource(Res.string.powered_by_open_source_software))
|
|
||||||
Text(
|
|
||||||
myStringResource(Res.string.view_the_open_source_licenses),
|
|
||||||
style = LocalTextStyle.current.merge(LinkStyle),
|
|
||||||
modifier = Modifier.clickable {
|
|
||||||
onRequestShowOpenSourceLibraries()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
Spacer(Modifier.height(8.dp))
|
|
||||||
Text(myStringResource(Res.string.localized_by_translators))
|
|
||||||
Text(
|
|
||||||
myStringResource(Res.string.meet_the_translators),
|
|
||||||
style = LocalTextStyle.current.merge(LinkStyle),
|
|
||||||
modifier = Modifier.clickable {
|
|
||||||
onRequestShowTranslators()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
AboutPageListItemButton(
|
||||||
|
itemModifier,
|
||||||
|
icon = MyIcons.openSource,
|
||||||
|
title = Res.string.powered_by_open_source_software.asStringSource(),
|
||||||
|
description = Res.string.view_the_open_source_licenses.asStringSource(),
|
||||||
|
onClick = {
|
||||||
|
onRequestShowOpenSourceLibraries()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
AboutPageListItemButton(
|
||||||
|
itemModifier,
|
||||||
|
icon = MyIcons.language,
|
||||||
|
title = Res.string.localized_by_translators.asStringSource(),
|
||||||
|
description = Res.string.meet_the_translators.asStringSource(),
|
||||||
|
onClick = {
|
||||||
|
onRequestShowTranslators()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SocialSmallButton(
|
||||||
|
icon: IconSource,
|
||||||
|
title: StringSource,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
Tooltip(title) {
|
||||||
|
IconActionButton(
|
||||||
|
icon,
|
||||||
|
contentDescription = title.rememberString(),
|
||||||
|
onClick = onClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun AboutPageListItemButton(
|
||||||
|
modifier: Modifier,
|
||||||
|
icon: IconSource,
|
||||||
|
title: StringSource,
|
||||||
|
description: StringSource,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
val shape = RoundedCornerShape(6.dp)
|
||||||
|
Row(
|
||||||
|
modifier
|
||||||
|
.clickable(onClick = onClick)
|
||||||
|
.border(1.dp, myColors.onBackground / 0.15f, shape)
|
||||||
|
.clip(shape)
|
||||||
|
.background(myColors.surface / 0.5f)
|
||||||
|
.padding(
|
||||||
|
horizontal = 8.dp,
|
||||||
|
vertical = 8.dp,
|
||||||
|
),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
MyIcon(
|
||||||
|
icon = icon,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(24.dp)
|
||||||
|
)
|
||||||
|
Spacer(Modifier.width(8.dp))
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
title.rememberString(),
|
||||||
|
fontSize = myTextSizes.base,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(2.dp))
|
||||||
|
Text(description.rememberString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun BoxScope.BackgroundEffects() {
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.align(Alignment.TopCenter)
|
||||||
|
.offset(x = (-50).dp, y = (-148).dp)
|
||||||
|
.fillMaxWidth(0.5f)
|
||||||
|
.height(250.dp)
|
||||||
|
.blur(
|
||||||
|
56.dp,
|
||||||
|
edgeTreatment = BlurredEdgeTreatment.Unbounded
|
||||||
|
)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(
|
||||||
|
myColors.primary / 0.15f
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.align(Alignment.BottomStart)
|
||||||
|
.size(220.dp)
|
||||||
|
.offset(x = (-64).dp, y = (+128).dp)
|
||||||
|
.blur(
|
||||||
|
56.dp,
|
||||||
|
edgeTreatment = BlurredEdgeTreatment.Unbounded
|
||||||
|
)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(
|
||||||
|
myColors.secondaryVariant / 0.15f
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.align(Alignment.BottomEnd)
|
||||||
|
.size(220.dp)
|
||||||
|
.offset(x = 32.dp, y = (-32).dp)
|
||||||
|
.blur(
|
||||||
|
56.dp,
|
||||||
|
edgeTreatment = BlurredEdgeTreatment.Unbounded
|
||||||
|
)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(
|
||||||
|
myColors.secondary / 0.15f
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LinkText(
|
fun LinkText(
|
||||||
text: String,
|
text: String,
|
||||||
link: String,
|
link: String,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
maxLines: Int = Int.MAX_VALUE,
|
maxLines: Int = Int.MAX_VALUE,
|
||||||
|
showExternalIndicator: Boolean = true,
|
||||||
overflow: TextOverflow = TextOverflow.Clip,
|
overflow: TextOverflow = TextOverflow.Clip,
|
||||||
) {
|
) {
|
||||||
val handler = LocalUriHandler.current
|
val handler = LocalUriHandler.current
|
||||||
@ -164,14 +414,16 @@ fun LinkText(
|
|||||||
overflow = overflow,
|
overflow = overflow,
|
||||||
maxLines = maxLines,
|
maxLines = maxLines,
|
||||||
)
|
)
|
||||||
MyIcon(
|
if (showExternalIndicator) {
|
||||||
MyIcons.externalLink,
|
MyIcon(
|
||||||
null,
|
MyIcons.externalLink,
|
||||||
Modifier.size(10.dp).alpha(
|
null,
|
||||||
if (isHovered) 0.75f
|
Modifier.size(10.dp).alpha(
|
||||||
else 0.5f
|
if (isHovered) 0.75f
|
||||||
|
else 0.5f
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,4 +92,7 @@ override val back get() = "/icons/back.svg".asIconSource()
|
|||||||
override val language: IconSource get() = "/icons/language.svg".asIconSource()
|
override val language: IconSource get() = "/icons/language.svg".asIconSource()
|
||||||
|
|
||||||
override val externalLink: IconSource get() = "/icons/external_link.svg".asIconSource()
|
override val externalLink: IconSource get() = "/icons/external_link.svg".asIconSource()
|
||||||
|
|
||||||
|
override val earth: IconSource get() = "/icons/earth.svg".asIconSource()
|
||||||
|
override val hearth: IconSource get() = "/icons/hearth.svg".asIconSource()
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Popup
|
import androidx.compose.ui.window.Popup
|
||||||
import androidx.compose.ui.window.rememberComponentRectPositionProvider
|
import androidx.compose.ui.window.rememberComponentRectPositionProvider
|
||||||
import com.abdownloadmanager.desktop.pages.settings.configurable.Configurable
|
|
||||||
import com.abdownloadmanager.desktop.ui.icon.MyIcons
|
import com.abdownloadmanager.desktop.ui.icon.MyIcons
|
||||||
import com.abdownloadmanager.desktop.ui.theme.myColors
|
import com.abdownloadmanager.desktop.ui.theme.myColors
|
||||||
import com.abdownloadmanager.desktop.ui.theme.myTextSizes
|
import com.abdownloadmanager.desktop.ui.theme.myTextSizes
|
||||||
@ -51,32 +50,10 @@ fun Help(
|
|||||||
tint = myColors.onSurface,
|
tint = myColors.onSurface,
|
||||||
)
|
)
|
||||||
if (showHelpContent) {
|
if (showHelpContent) {
|
||||||
Popup(
|
TooltipPopup(
|
||||||
popupPositionProvider = rememberComponentRectPositionProvider(
|
onRequestCloseShowHelpContent = onRequestCloseShowHelpContent,
|
||||||
anchor = Alignment.TopCenter,
|
content = content,
|
||||||
alignment = Alignment.TopCenter,
|
)
|
||||||
),
|
|
||||||
onDismissRequest = onRequestCloseShowHelpContent
|
|
||||||
) {
|
|
||||||
val shape = RoundedCornerShape(6.dp)
|
|
||||||
Box(
|
|
||||||
Modifier
|
|
||||||
.padding(vertical = 4.dp)
|
|
||||||
.widthIn(max = 240.dp)
|
|
||||||
.shadow(24.dp)
|
|
||||||
.clip(shape)
|
|
||||||
.border(1.dp, myColors.surface, shape)
|
|
||||||
.background(myColors.menuGradientBackground)
|
|
||||||
.padding(8.dp)
|
|
||||||
) {
|
|
||||||
WithContentColor(myColors.onSurface) {
|
|
||||||
Text(
|
|
||||||
content,
|
|
||||||
fontSize = myTextSizes.base,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
package com.abdownloadmanager.desktop.ui.widget
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.hoverable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.interaction.collectIsHoveredAsState
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.widthIn
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.draw.shadow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Popup
|
||||||
|
import androidx.compose.ui.window.rememberComponentRectPositionProvider
|
||||||
|
import com.abdownloadmanager.desktop.ui.theme.myColors
|
||||||
|
import com.abdownloadmanager.desktop.ui.theme.myTextSizes
|
||||||
|
import com.abdownloadmanager.utils.compose.WithContentColor
|
||||||
|
import ir.amirab.util.compose.StringSource
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Tooltip(
|
||||||
|
tooltip: StringSource,
|
||||||
|
delayUntilShow: Long = 500,
|
||||||
|
content: @Composable () -> Unit,
|
||||||
|
) {
|
||||||
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
val isHovered by interactionSource.collectIsHoveredAsState()
|
||||||
|
var showHint by remember { mutableStateOf(false) }
|
||||||
|
LaunchedEffect(isHovered) {
|
||||||
|
if (isHovered) {
|
||||||
|
delay(delayUntilShow)
|
||||||
|
showHint = true
|
||||||
|
} else {
|
||||||
|
showHint = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.hoverable(interactionSource)
|
||||||
|
) {
|
||||||
|
if (showHint) {
|
||||||
|
TooltipPopup(
|
||||||
|
onRequestCloseShowHelpContent = {
|
||||||
|
showHint = false
|
||||||
|
},
|
||||||
|
content = tooltip.rememberString(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TooltipPopup(
|
||||||
|
onRequestCloseShowHelpContent: () -> Unit,
|
||||||
|
content: String,
|
||||||
|
) {
|
||||||
|
Popup(
|
||||||
|
popupPositionProvider = rememberComponentRectPositionProvider(
|
||||||
|
anchor = Alignment.TopCenter,
|
||||||
|
alignment = Alignment.TopCenter,
|
||||||
|
),
|
||||||
|
onDismissRequest = onRequestCloseShowHelpContent
|
||||||
|
) {
|
||||||
|
val shape = RoundedCornerShape(6.dp)
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.padding(vertical = 4.dp)
|
||||||
|
.widthIn(max = 240.dp)
|
||||||
|
.shadow(24.dp)
|
||||||
|
.clip(shape)
|
||||||
|
.border(1.dp, myColors.surface, shape)
|
||||||
|
.background(myColors.menuGradientBackground)
|
||||||
|
.padding(8.dp)
|
||||||
|
) {
|
||||||
|
WithContentColor(myColors.onSurface) {
|
||||||
|
Text(
|
||||||
|
content,
|
||||||
|
fontSize = myTextSizes.base,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
desktop/app/src/main/resources/icons/hearth.svg
Normal file
4
desktop/app/src/main/resources/icons/hearth.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M19.5001 12.572L12.0001 20L4.50006 12.572C4.00536 12.0906 3.6157 11.512 3.3556 10.8726C3.09551 10.2333 2.97062 9.54694 2.98879 8.85693C3.00697 8.16691 3.16782 7.48813 3.46121 6.86333C3.75461 6.23853 4.17419 5.68125 4.69354 5.22657C5.21289 4.7719 5.82076 4.42968 6.47887 4.22147C7.13697 4.01327 7.83106 3.94358 8.51743 4.0168C9.20379 4.09001 9.86756 4.30455 10.4669 4.6469C11.0663 4.98925 11.5883 5.45199 12.0001 6.00599C12.4136 5.45602 12.9362 4.99731 13.5352 4.6586C14.1341 4.31988 14.7966 4.10844 15.481 4.03751C16.1654 3.96658 16.8571 4.03769 17.5128 4.24639C18.1685 4.45508 18.7741 4.79687 19.2916 5.25036C19.8091 5.70385 20.2275 6.25928 20.5205 6.88189C20.8135 7.50449 20.9748 8.18088 20.9944 8.8687C21.0139 9.55653 20.8913 10.241 20.6342 10.8792C20.3771 11.5175 19.991 12.0958 19.5001 12.578"
|
||||||
|
stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1006 B |
@ -79,4 +79,7 @@ interface IMyIcons {
|
|||||||
val network: IconSource
|
val network: IconSource
|
||||||
val language: IconSource
|
val language: IconSource
|
||||||
val externalLink: IconSource
|
val externalLink: IconSource
|
||||||
|
|
||||||
|
val earth: IconSource
|
||||||
|
val hearth: IconSource
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user