Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

💄 Implement the basic framework of pasteboard preview UI #345

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

10 changes: 10 additions & 0 deletions composeApp/src/commonMain/kotlin/com/clipevery/dao/clip/ClipDao.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.clipevery.dao.clip

import io.realm.kotlin.query.RealmResults
import io.realm.kotlin.types.RealmInstant
import org.mongodb.kbson.ObjectId

interface ClipDao {
Expand All @@ -9,4 +11,12 @@ interface ClipDao {
fun createClipData(clipData: ClipData)

fun deleteClipData(id: ObjectId)

fun getClipData(appInstanceId: String? = null,
limit: Int): RealmResults<ClipData>

fun getClipData(appInstanceId: String?,
limit: Int,
createTime: RealmInstant,
excludeClipId: List<Int>): RealmResults<ClipData>
}
71 changes: 3 additions & 68 deletions composeApp/src/commonMain/kotlin/com/clipevery/ui/TabsView.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.clipevery.ui

import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
Expand All @@ -11,28 +10,15 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.GenericShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Tab
import androidx.compose.material.TabRow
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
Expand All @@ -41,7 +27,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.clipevery.LocalKoinApplication
import com.clipevery.i18n.GlobalCopywriter
import com.clipevery.ui.clip.ClipPreviewView
import com.clipevery.ui.clip.ClipPreviewsView
import com.clipevery.ui.devices.DevicesView
import com.clipevery.ui.devices.bindingQRCode

Expand All @@ -67,10 +53,10 @@ fun TabsView(currentPageViewContext: MutableState<PageViewContext>) {

Column(modifier = Modifier.fillMaxSize()) {
when (currentPageViewContext.value.pageViewType) {
PageViewType.CLIP_PREVIEW -> ClipPreviewView()
PageViewType.CLIP_PREVIEW -> ClipPreviewsView()
PageViewType.DEVICE_PREVIEW -> DevicesView(currentPageViewContext)
PageViewType.QR_CODE -> bindingQRCode()
else -> ClipPreviewView()
else -> ClipPreviewsView()
}
}
}
Expand Down Expand Up @@ -114,54 +100,3 @@ fun TabView(currentPageViewContext: MutableState<PageViewContext>, pageViewType:
)
}
}


@Composable
@Preview
fun PreviewTabsView() {
var selectedTabIndex by remember { mutableStateOf(0) }
var searchText by remember { mutableStateOf("") }
val tabTitles = listOf("Tab 1", "Tab 2", "Tab 3")

Row(modifier = Modifier.padding(8.dp).fillMaxWidth()) {
Column(modifier = Modifier.padding(8.dp).wrapContentWidth().wrapContentHeight()) {
TabRow(
modifier = Modifier.wrapContentWidth().wrapContentHeight(),
selectedTabIndex = selectedTabIndex,
backgroundColor = Color.White
) {
tabTitles.forEachIndexed { index, title ->
Tab(
modifier = Modifier.wrapContentWidth().wrapContentHeight(),
selected = index == selectedTabIndex,
onClick = { selectedTabIndex = index },
text = { Text(modifier = Modifier.wrapContentWidth(), text = title, fontSize = 12.sp) }
)
}
}
}
BasicTextField(
value = searchText,
onValueChange = { searchText = it },
modifier = Modifier.width(100.dp), // Set a fixed width for the TextField
decorationBox = { innerTextField ->
Row {
Icon(Icons.Filled.Search, contentDescription = "Search Icon")
Box { innerTextField() }
}
}
)
}
Column() {
when (selectedTabIndex) {
0 -> TabContent("Content for Tab 1")
1 -> TabContent("Content for Tab 2")
2 -> TabContent("Content for Tab 3")
}
}
}

@Composable
fun TabContent(text: String) {
Text(text = text)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,22 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.onPointerEvent
import androidx.compose.ui.unit.dp
import com.clipevery.clip.item.ClipItem
import com.clipevery.clip.item.ClipText
import compose.icons.TablerIcons
import compose.icons.tablericons.FileText
import com.clipevery.dao.clip.ClipData

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun ClipPreviewItemView(clipItem: ClipItem) {
fun ClipPreviewItemView(clipData: ClipData) {

var hover by remember { mutableStateOf(false) }
val backgroundColor = if (hover) MaterialTheme.colors.secondaryVariant else MaterialTheme.colors.background
Expand All @@ -47,22 +41,8 @@ fun ClipPreviewItemView(clipItem: ClipItem) {
)
.background(backgroundColor)) {

ClipPreviewItemDetail(clipItem)
ClipPreviewView(clipData) {
ClipPreview(clipData)
}
}
}

@Composable
fun ClipPreviewItemDetail(clipItem: ClipItem) {
if (clipItem is ClipText) {
TextClipItem(clipItem)
}
}

@Composable
fun TextClipItem(textClipItem: ClipText) {
Icon(
imageVector = TablerIcons.FileText,
contentDescription = "clip text",
tint = Color.Gray)
Text(text = textClipItem.text)
}
Original file line number Diff line number Diff line change
@@ -1,108 +1,69 @@
package com.clipevery.ui.clip

import androidx.compose.foundation.ScrollbarStyle
import androidx.compose.foundation.VerticalScrollbar
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.draggable
import androidx.compose.foundation.gestures.rememberDraggableState
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollbarAdapter
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.layout.height
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.clipevery.clip.item.ClipItem
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import com.clipevery.dao.clip.ClipAppearItem
import com.clipevery.dao.clip.ClipContent
import com.clipevery.dao.clip.ClipData
import com.clipevery.dao.clip.ClipType
import kotlin.reflect.KClass
import kotlin.reflect.cast

@Composable
fun ClipPreviewView() {
val listState = rememberLazyListState()
var isScrolling by remember { mutableStateOf(false) }
var scrollJob: Job? by remember { mutableStateOf(null) }
val coroutineScope = rememberCoroutineScope()
val clipItems = remember { mutableStateListOf<ClipItem>() }

LaunchedEffect(Unit) {
loadMoreItems(clipItems)
fun <T: Any> ClipData.getClipItem(kclass: KClass<T>): T? {
return ClipContent.getClipItem(this.clipAppearContent)?.let {
if (kclass.isInstance(it)) {
kclass.cast(it)
} else {
null
}
}
}

LaunchedEffect(listState) {
snapshotFlow { listState.layoutInfo.visibleItemsInfo }
.collect { visibleItems ->
if (visibleItems.isNotEmpty() && visibleItems.last().index == clipItems.size - 1) {
// 当滚动到列表底部时加载更多数据
loadMoreItems(clipItems)
}
isScrolling = true
scrollJob?.cancel()
scrollJob = coroutineScope.launch {
delay(1000)
isScrolling = false
}
}
}
fun ClipData.getClipItem(): ClipAppearItem? {
return ClipContent.getClipItem(this.clipAppearContent)
}

Box(modifier = Modifier.fillMaxWidth()) {
@Composable
fun ClipPreviewView(clipData: ClipData, clipContent: @Composable ClipAppearItem.() -> Unit) {
clipData.getClipItem()?.let {
Column(modifier = Modifier.fillMaxWidth()
.height(150.dp)
) {
Row(modifier = Modifier.fillMaxWidth()
.height(120.dp)
) {
it.clipContent()
Column(modifier = Modifier.fillMaxHeight()) {
Row() {

}
}
}
Row(modifier = Modifier.fillMaxWidth()
.background(color = MaterialTheme.colors.surface)
.height(30.dp)
) {

LazyColumn(
state = listState,
modifier = Modifier.wrapContentHeight()
) {
items(clipItems) { clipItem ->
ClipPreviewItemView(clipItem)
}
}

VerticalScrollbar(
modifier = Modifier.background(color = Color.Transparent)
.fillMaxHeight().align(Alignment.CenterEnd)
.draggable(
orientation = Orientation.Vertical,
state = rememberDraggableState { delta ->
coroutineScope.launch {
listState.scrollBy(-delta)
}
},
),
adapter = rememberScrollbarAdapter(scrollState = listState),
style = ScrollbarStyle(
minimalHeight = 16.dp,
thickness = 8.dp,
shape = RoundedCornerShape(4.dp),
hoverDurationMillis = 300,
unhoverColor = if (isScrolling) MaterialTheme.colors.onBackground.copy(alpha = 0.48f) else Color.Transparent,
hoverColor = MaterialTheme.colors.onBackground
)
)
}
}

fun loadMoreItems(items: MutableList<ClipItem>) {
// 这里添加数据加载逻辑,如网络请求或数据库查询
// 示例:添加新数据到列表
// if (items.size < 20) {
// items += TextClipItem("New Item " + items.size)
// }
@Composable
fun ClipPreview(clipData: ClipData) {
if (clipData.preCreate) {
// todo preCreate
} else {
when(clipData.clipType) {
ClipType.TEXT -> TextPreviewView(clipData)
}
}
}
Loading
Loading