Compare commits

...

1 Commits

Author SHA1 Message Date
Avently
95f4a8c906 desktop: responsive design for large and small screens 2023-07-28 18:14:36 +07:00
10 changed files with 61 additions and 29 deletions

View File

@@ -50,4 +50,7 @@ actual fun screenWidth(): Dp = LocalConfiguration.current.screenWidthDp.dp
actual fun desktopExpandWindowToWidth(width: Dp) {} actual fun desktopExpandWindowToWidth(width: Dp) {}
@Composable
actual fun allowToShowBackButtonInCenter(): Boolean = true
actual fun isRtl(text: CharSequence): Boolean = BidiFormatter.getInstance().isRtl(text) actual fun isRtl(text: CharSequence): Boolean = BidiFormatter.getInstance().isRtl(text)

View File

@@ -274,25 +274,43 @@ fun EndPartOfScreen() {
@Composable @Composable
fun DesktopScreen(settingsState: SettingsViewState) { fun DesktopScreen(settingsState: SettingsViewState) {
BoxWithConstraints {
Box { Box {
val maxWidth = this@BoxWithConstraints.maxWidth
// 56.dp is a size of unused space of settings drawer // 56.dp is a size of unused space of settings drawer
Box(Modifier.width(DEFAULT_START_MODAL_WIDTH + 56.dp)) { val startMaxWidth = when {
StartPartOfScreen(settingsState) maxWidth >= DEFAULT_START_MODAL_WIDTH + DEFAULT_MIN_CENTER_MODAL_WIDTH -> DEFAULT_START_MODAL_WIDTH + 56.dp
ChatModel.chatId.value == null && !ModalManager.center.hasModalsOpen() -> maxWidth
else -> DEFAULT_START_MODAL_WIDTH
} }
Box(Modifier.widthIn(max = DEFAULT_START_MODAL_WIDTH)) { val startModalMaxWidth = when {
maxWidth >= DEFAULT_START_MODAL_WIDTH + DEFAULT_MIN_CENTER_MODAL_WIDTH -> DEFAULT_START_MODAL_WIDTH
ChatModel.chatId.value == null && !ModalManager.center.hasModalsOpen() -> maxWidth
else -> DEFAULT_START_MODAL_WIDTH
}
Box(Modifier.widthIn(max = startMaxWidth)) {
StartPartOfScreen(settingsState)
Box(Modifier.widthIn(max = startModalMaxWidth)) {
ModalManager.start.showInView() ModalManager.start.showInView()
} }
Row(Modifier.padding(start = DEFAULT_START_MODAL_WIDTH).clipToBounds()) { }
val centerMaxWidth = when {
maxWidth >= DEFAULT_START_MODAL_WIDTH + DEFAULT_MIN_CENTER_MODAL_WIDTH -> maxWidth - DEFAULT_START_MODAL_WIDTH
ChatModel.chatId.value != null || ModalManager.center.hasModalsOpen() -> maxOf(DEFAULT_START_MODAL_WIDTH, maxWidth) - 1.dp
else -> 0.dp
}
Row(Modifier.padding(start = maxOf(maxWidth - centerMaxWidth, 0.dp))) {
Box(Modifier.widthIn(min = DEFAULT_MIN_CENTER_MODAL_WIDTH).weight(1f)) { Box(Modifier.widthIn(min = DEFAULT_MIN_CENTER_MODAL_WIDTH).weight(1f)) {
CenterPartOfScreen() CenterPartOfScreen()
} }
if (ModalManager.end.hasModalsOpen()) { if (ModalManager.end.hasModalsOpen() && maxWidth >= DEFAULT_START_MODAL_WIDTH + DEFAULT_MIN_CENTER_MODAL_WIDTH + DEFAULT_END_MODAL_WIDTH) {
VerticalDivider() VerticalDivider()
} }
Box(Modifier.widthIn(max = DEFAULT_END_MODAL_WIDTH).clipToBounds()) { Box(Modifier.widthIn(max = if (centerMaxWidth < DEFAULT_MIN_CENTER_MODAL_WIDTH + DEFAULT_END_MODAL_WIDTH) centerMaxWidth else DEFAULT_END_MODAL_WIDTH).clipToBounds()) {
EndPartOfScreen() EndPartOfScreen()
} }
} }
}
val (userPickerState, scaffoldState, switchingUsers) = settingsState val (userPickerState, scaffoldState, switchingUsers) = settingsState
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
if (scaffoldState.drawerState.isOpen) { if (scaffoldState.drawerState.isOpen) {
@@ -306,7 +324,9 @@ fun DesktopScreen(settingsState: SettingsViewState) {
}) })
) )
} }
if (maxWidth >= DEFAULT_START_MODAL_WIDTH + DEFAULT_MIN_CENTER_MODAL_WIDTH) {
VerticalDivider(Modifier.padding(start = DEFAULT_START_MODAL_WIDTH)) VerticalDivider(Modifier.padding(start = DEFAULT_START_MODAL_WIDTH))
}
UserPicker(chatModel, userPickerState, switchingUsers) { UserPicker(chatModel, userPickerState, switchingUsers) {
scope.launch { if (scaffoldState.drawerState.isOpen) scaffoldState.drawerState.close() else scaffoldState.drawerState.open() } scope.launch { if (scaffoldState.drawerState.isOpen) scaffoldState.drawerState.close() else scaffoldState.drawerState.open() }
} }

View File

@@ -5,6 +5,7 @@ import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import chat.simplex.common.ui.theme.DEFAULT_MIN_CENTER_MODAL_WIDTH
import com.russhwolf.settings.Settings import com.russhwolf.settings.Settings
import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.StringResource
@@ -30,4 +31,7 @@ expect fun screenWidth(): Dp
expect fun desktopExpandWindowToWidth(width: Dp) expect fun desktopExpandWindowToWidth(width: Dp)
@Composable
expect fun allowToShowBackButtonInCenter(): Boolean
expect fun isRtl(text: CharSequence): Boolean expect fun isRtl(text: CharSequence): Boolean

View File

@@ -474,7 +474,7 @@ fun ChatInfoToolbar(
showSearch = false showSearch = false
} }
} }
if (appPlatform.isAndroid) { if (allowToShowBackButtonInCenter()) {
BackHandler(onBack = onBackClicked) BackHandler(onBack = onBackClicked)
} }
val barButtons = arrayListOf<@Composable RowScope.() -> Unit>() val barButtons = arrayListOf<@Composable RowScope.() -> Unit>()
@@ -534,7 +534,7 @@ fun ChatInfoToolbar(
} }
DefaultTopAppBar( DefaultTopAppBar(
navigationButton = { if (appPlatform.isAndroid || showSearch) { NavigationButtonBack(onBackClicked) } }, navigationButton = { if (allowToShowBackButtonInCenter() || showSearch) { Box(Modifier.offset(y = 4.dp)) { NavigationButtonBack(onBackClicked) } } },
title = { ChatInfoToolbarTitle(chat.chatInfo) }, title = { ChatInfoToolbarTitle(chat.chatInfo) },
onTitleClick = info, onTitleClick = info,
showSearch = showSearch, showSearch = showSearch,

View File

@@ -16,6 +16,7 @@ import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import chat.simplex.common.model.* import chat.simplex.common.model.*
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.* import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.chat.* import chat.simplex.common.views.chat.*
import chat.simplex.common.views.chat.group.deleteGroupDialog import chat.simplex.common.views.chat.group.deleteGroupDialog
@@ -24,8 +25,6 @@ import chat.simplex.common.views.chat.item.InvalidJSONView
import chat.simplex.common.views.chat.item.ItemAction import chat.simplex.common.views.chat.item.ItemAction
import chat.simplex.common.views.helpers.* import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.newchat.ContactConnectionInfoView import chat.simplex.common.views.newchat.ContactConnectionInfoView
import chat.simplex.common.platform.appPlatform
import chat.simplex.common.platform.ntfManager
import chat.simplex.res.MR import chat.simplex.res.MR
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
@@ -42,6 +41,7 @@ fun ChatListNavLinkView(chat: Chat, chatModel: ChatModel) {
showMenu.value = false showMenu.value = false
delay(500L) delay(500L)
} }
val showClose = allowToShowBackButtonInCenter()
when (chat.chatInfo) { when (chat.chatInfo) {
is ChatInfo.Direct -> { is ChatInfo.Direct -> {
val contactNetworkStatus = chatModel.contactNetworkStatus(chat.chatInfo.contact) val contactNetworkStatus = chatModel.contactNetworkStatus(chat.chatInfo.contact)
@@ -75,7 +75,7 @@ fun ChatListNavLinkView(chat: Chat, chatModel: ChatModel) {
click = { click = {
ModalManager.center.closeModals() ModalManager.center.closeModals()
ModalManager.end.closeModals() ModalManager.end.closeModals()
ModalManager.center.showModalCloseable(true, showClose = appPlatform.isAndroid) { close -> ModalManager.center.showModalCloseable(true, showClose = showClose) { close ->
ContactConnectionInfoView(chatModel, chat.chatInfo.contactConnection.connReqInv, chat.chatInfo.contactConnection, false, close) ContactConnectionInfoView(chatModel, chat.chatInfo.contactConnection.connReqInv, chat.chatInfo.contactConnection, false, close)
} }
}, },
@@ -341,13 +341,14 @@ fun ContactRequestMenuItems(chatInfo: ChatInfo.ContactRequest, chatModel: ChatMo
@Composable @Composable
fun ContactConnectionMenuItems(chatInfo: ChatInfo.ContactConnection, chatModel: ChatModel, showMenu: MutableState<Boolean>) { fun ContactConnectionMenuItems(chatInfo: ChatInfo.ContactConnection, chatModel: ChatModel, showMenu: MutableState<Boolean>) {
val showClose = allowToShowBackButtonInCenter()
ItemAction( ItemAction(
stringResource(MR.strings.set_contact_name), stringResource(MR.strings.set_contact_name),
painterResource(MR.images.ic_edit), painterResource(MR.images.ic_edit),
onClick = { onClick = {
ModalManager.center.closeModals() ModalManager.center.closeModals()
ModalManager.end.closeModals() ModalManager.end.closeModals()
ModalManager.center.showModalCloseable(true, showClose = appPlatform.isAndroid) { close -> ModalManager.center.showModalCloseable(true, showClose = showClose) { close ->
ContactConnectionInfoView(chatModel, chatInfo.contactConnection.connReqInv, chatInfo.contactConnection, true, close) ContactConnectionInfoView(chatModel, chatInfo.contactConnection.connReqInv, chatInfo.contactConnection, true, close)
} }
showMenu.value = false showMenu.value = false

View File

@@ -58,7 +58,7 @@ fun ChatListView(chatModel: ChatModel, settingsState: SettingsViewState, setPerf
connectIfOpenedViaUri(url, chatModel) connectIfOpenedViaUri(url, chatModel)
} }
} }
val endPadding = if (appPlatform.isDesktop) 56.dp else 0.dp val endPadding = if (appPlatform.isDesktop && !allowToShowBackButtonInCenter()) 56.dp else 0.dp
var searchInList by rememberSaveable { mutableStateOf("") } var searchInList by rememberSaveable { mutableStateOf("") }
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val (userPickerState, scaffoldState, switchingUsers ) = settingsState val (userPickerState, scaffoldState, switchingUsers ) = settingsState

View File

@@ -18,8 +18,7 @@ import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.helpers.* import chat.simplex.common.views.helpers.*
import chat.simplex.common.model.Chat import chat.simplex.common.model.Chat
import chat.simplex.common.model.ChatModel import chat.simplex.common.model.ChatModel
import chat.simplex.common.platform.BackHandler import chat.simplex.common.platform.*
import chat.simplex.common.platform.appPlatform
import chat.simplex.res.MR import chat.simplex.res.MR
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@@ -27,7 +26,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
fun ShareListView(chatModel: ChatModel, settingsState: SettingsViewState, stopped: Boolean) { fun ShareListView(chatModel: ChatModel, settingsState: SettingsViewState, stopped: Boolean) {
var searchInList by rememberSaveable { mutableStateOf("") } var searchInList by rememberSaveable { mutableStateOf("") }
val (userPickerState, scaffoldState, switchingUsers) = settingsState val (userPickerState, scaffoldState, switchingUsers) = settingsState
val endPadding = if (appPlatform.isDesktop) 56.dp else 0.dp val endPadding = if (appPlatform.isDesktop && !allowToShowBackButtonInCenter()) 56.dp else 0.dp
Scaffold( Scaffold(
Modifier.padding(end = endPadding), Modifier.padding(end = endPadding),
scaffoldState = scaffoldState, scaffoldState = scaffoldState,

View File

@@ -96,7 +96,7 @@ private fun NewChatSheetLayout(
} }
} }
} }
val endPadding = if (appPlatform.isDesktop) 56.dp else 0.dp val endPadding = if (appPlatform.isDesktop && !allowToShowBackButtonInCenter()) 56.dp else 0.dp
val maxWidth = with(LocalDensity.current) { screenWidth() * density } val maxWidth = with(LocalDensity.current) { screenWidth() * density }
Column( Column(
Modifier Modifier

View File

@@ -73,7 +73,7 @@ private fun SetDeliveryReceiptsLayout(
skip: () -> Unit, skip: () -> Unit,
userCount: Int, userCount: Int,
) { ) {
val endPadding = if (appPlatform.isDesktop) 56.dp else 0.dp val endPadding = if (appPlatform.isDesktop && !allowToShowBackButtonInCenter()) 56.dp else 0.dp
Column( Column(
Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(top = DEFAULT_PADDING, end = endPadding), Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(top = DEFAULT_PADDING, end = endPadding),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,

View File

@@ -6,6 +6,8 @@ import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.* import androidx.compose.ui.unit.*
import chat.simplex.common.simplexWindowState import chat.simplex.common.simplexWindowState
import chat.simplex.common.ui.theme.DEFAULT_MIN_CENTER_MODAL_WIDTH
import chat.simplex.common.ui.theme.DEFAULT_START_MODAL_WIDTH
import com.russhwolf.settings.* import com.russhwolf.settings.*
import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.StringResource
import dev.icerock.moko.resources.desc.desc import dev.icerock.moko.resources.desc.desc
@@ -54,6 +56,9 @@ actual fun desktopExpandWindowToWidth(width: Dp) {
simplexWindowState.windowState.size = simplexWindowState.windowState.size.copy(width = width) simplexWindowState.windowState.size = simplexWindowState.windowState.size.copy(width = width)
} }
@Composable
actual fun allowToShowBackButtonInCenter(): Boolean = simplexWindowState.windowState.size.width < DEFAULT_START_MODAL_WIDTH + DEFAULT_MIN_CENTER_MODAL_WIDTH
actual fun isRtl(text: CharSequence): Boolean { actual fun isRtl(text: CharSequence): Boolean {
if (text.isEmpty()) return false if (text.isEmpty()) return false
return text.any { char -> return text.any { char ->