Skip to content

Commit ddfc431

Browse files
committed
Refactor BrowserArea.VerticaTabs into Tabs.Vertical
1 parent ea349da commit ddfc431

File tree

2 files changed

+82
-93
lines changed

2 files changed

+82
-93
lines changed

view/BrowserArea.kt

Lines changed: 4 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -18,54 +18,29 @@
1818

1919
package com.vaticle.typedb.studio.view
2020

21-
import androidx.compose.foundation.background
22-
import androidx.compose.foundation.clickable
23-
import androidx.compose.foundation.layout.Arrangement
24-
import androidx.compose.foundation.layout.Box
25-
import androidx.compose.foundation.layout.Column
2621
import androidx.compose.foundation.layout.Row
27-
import androidx.compose.foundation.layout.Spacer
2822
import androidx.compose.foundation.layout.fillMaxHeight
2923
import androidx.compose.foundation.layout.fillMaxSize
30-
import androidx.compose.foundation.layout.fillMaxWidth
31-
import androidx.compose.foundation.layout.height
32-
import androidx.compose.foundation.layout.offset
33-
import androidx.compose.foundation.layout.requiredWidth
34-
import androidx.compose.foundation.layout.width
3524
import androidx.compose.runtime.Composable
3625
import androidx.compose.runtime.getValue
3726
import androidx.compose.runtime.mutableStateOf
3827
import androidx.compose.runtime.remember
3928
import androidx.compose.runtime.setValue
40-
import androidx.compose.ui.Alignment
41-
import androidx.compose.ui.ExperimentalComposeUiApi
4229
import androidx.compose.ui.Modifier
43-
import androidx.compose.ui.draw.rotate
44-
import androidx.compose.ui.graphics.Color
45-
import androidx.compose.ui.input.pointer.PointerIconDefaults
46-
import androidx.compose.ui.input.pointer.pointerHoverIcon
4730
import androidx.compose.ui.unit.Dp
4831
import androidx.compose.ui.unit.dp
49-
import androidx.compose.ui.unit.sp
5032
import com.vaticle.typedb.common.collection.Either
51-
import com.vaticle.typedb.studio.view.common.theme.Theme
5233
import com.vaticle.typedb.studio.view.material.Browser
53-
import com.vaticle.typedb.studio.view.material.Form.Text
5434
import com.vaticle.typedb.studio.view.material.Frame
55-
import com.vaticle.typedb.studio.view.material.Icon
5635
import com.vaticle.typedb.studio.view.material.Separator
36+
import com.vaticle.typedb.studio.view.material.Tabs
5737
import com.vaticle.typedb.studio.view.project.ProjectBrowser
5838
import com.vaticle.typedb.studio.view.types.TypeBrowser
5939

6040
object BrowserArea {
6141

6242
val WIDTH = 300.dp
6343
val MIN_WIDTH = 120.dp
64-
private val SIDE_TAB_WIDTH = 22.dp
65-
private val SIDE_TAB_HEIGHT = 100.dp
66-
private val SIDE_TAB_SPACING = 8.dp
67-
private val ICON_SIZE = 10.sp
68-
private val TAB_OFFSET = (-40).dp
6944

7045
class State constructor(private val paneState: Frame.PaneState) {
7146

@@ -82,7 +57,7 @@ object BrowserArea {
8257
fun mayUpdatePaneState() {
8358
if (openedBrowsers.isEmpty()) {
8459
unfreezeSize = paneState.size
85-
paneState.freeze(SIDE_TAB_WIDTH)
60+
paneState.freeze(Tabs.Vertical.WIDTH)
8661
} else if (paneState.isFrozen) paneState.unfreeze(unfreezeSize)
8762
}
8863
}
@@ -92,9 +67,9 @@ object BrowserArea {
9267
val state = remember { State(paneState) }
9368
val openedBrowsers = state.openedBrowsers
9469
Row(Modifier.fillMaxSize()) {
95-
VerticalTabs(
70+
Tabs.Vertical.Layout(
9671
tabs = state.browsers,
97-
position = Position.LEFT,
72+
position = Tabs.Vertical.Position.LEFT,
9873
labelFn = { it.label },
9974
iconFn = { it.icon },
10075
isActiveFn = { it.isOpen }
@@ -116,56 +91,4 @@ object BrowserArea {
11691
}
11792
}
11893
}
119-
120-
enum class Position(internal val degree: Float) { LEFT(-90f), RIGHT(90f) }
121-
122-
@Composable
123-
private fun <T : Any> VerticalTabs(
124-
tabs: List<T>,
125-
position: Position,
126-
labelFn: (T) -> String,
127-
iconFn: (T) -> Icon.Code,
128-
isActiveFn: (T) -> Boolean,
129-
onClick: (T) -> Unit,
130-
) {
131-
Column(Modifier.width(SIDE_TAB_WIDTH), verticalArrangement = Arrangement.Top) {
132-
tabs.forEach { Tab(it, position, labelFn, iconFn, isActiveFn, onClick) }
133-
}
134-
}
135-
136-
@OptIn(ExperimentalComposeUiApi::class)
137-
@Composable
138-
private fun <T : Any> Tab(
139-
tab: T,
140-
position: Position,
141-
labelFn: (T) -> String,
142-
iconFn: (T) -> Icon.Code,
143-
isActiveFn: (T) -> Boolean,
144-
onClick: (T) -> Unit,
145-
) {
146-
@Composable
147-
fun bgColor(): Color = if (isActiveFn(tab)) Theme.studio.surface else Theme.studio.backgroundDark
148-
Box(
149-
modifier = Modifier
150-
.fillMaxWidth()
151-
.height(SIDE_TAB_HEIGHT)
152-
.pointerHoverIcon(PointerIconDefaults.Hand)
153-
.clickable { onClick(tab) }
154-
) {
155-
Row(
156-
verticalAlignment = Alignment.CenterVertically,
157-
modifier = Modifier.requiredWidth(SIDE_TAB_HEIGHT)
158-
.rotate(position.degree)
159-
.offset(x = TAB_OFFSET)
160-
.background(color = bgColor())
161-
) {
162-
Spacer(modifier = Modifier.weight(1f))
163-
Icon.Render(icon = iconFn(tab), size = ICON_SIZE)
164-
Spacer(modifier = Modifier.width(SIDE_TAB_SPACING))
165-
Text(value = labelFn(tab))
166-
Spacer(modifier = Modifier.weight(1f))
167-
}
168-
}
169-
Separator.Horizontal()
170-
}
17194
}

view/material/Tabs.kt

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@ package com.vaticle.typedb.studio.view.material
2020

2121
import androidx.compose.foundation.ScrollState
2222
import androidx.compose.foundation.background
23+
import androidx.compose.foundation.clickable
2324
import androidx.compose.foundation.horizontalScroll
25+
import androidx.compose.foundation.layout.Arrangement
2426
import androidx.compose.foundation.layout.Box
2527
import androidx.compose.foundation.layout.Column
2628
import androidx.compose.foundation.layout.Row
2729
import androidx.compose.foundation.layout.Spacer
2830
import androidx.compose.foundation.layout.fillMaxWidth
2931
import androidx.compose.foundation.layout.height
32+
import androidx.compose.foundation.layout.offset
33+
import androidx.compose.foundation.layout.requiredWidth
3034
import androidx.compose.foundation.layout.size
3135
import androidx.compose.foundation.layout.width
3236
import androidx.compose.foundation.layout.widthIn
@@ -39,6 +43,7 @@ import androidx.compose.runtime.setValue
3943
import androidx.compose.ui.Alignment
4044
import androidx.compose.ui.ExperimentalComposeUiApi
4145
import androidx.compose.ui.Modifier
46+
import androidx.compose.ui.draw.rotate
4247
import androidx.compose.ui.graphics.Color
4348
import androidx.compose.ui.input.pointer.PointerIconDefaults
4449
import androidx.compose.ui.input.pointer.PointerInputScope
@@ -52,8 +57,6 @@ import androidx.compose.ui.unit.dp
5257
import androidx.compose.ui.unit.sp
5358
import com.vaticle.typedb.studio.view.common.Util.toDP
5459
import com.vaticle.typedb.studio.view.common.theme.Theme
55-
import com.vaticle.typedb.studio.view.common.theme.Theme.PANEL_BAR_HEIGHT
56-
import com.vaticle.typedb.studio.view.common.theme.Theme.PANEL_BAR_SPACING
5760
import com.vaticle.typedb.studio.view.material.Form.IconArg
5861
import com.vaticle.typedb.studio.view.material.Form.IconButtonArg
5962
import java.awt.event.MouseEvent
@@ -64,10 +67,73 @@ object Tabs {
6467

6568
private val TAB_UNDERLINE_HEIGHT = 2.dp
6669
private val TAB_SCROLL_DELTA = 200.dp
67-
private val ICON_SIZE = 10.sp
70+
private val TAB_ICON_SIZE = 10.sp
71+
private val TAB_SPACING = Theme.PANEL_BAR_SPACING
72+
73+
object Vertical {
74+
75+
val WIDTH = 22.dp
76+
private val TAB_HEIGHT = 100.dp
77+
private val TAB_OFFSET = (-40).dp
78+
79+
enum class Position(internal val degree: Float) { LEFT(-90f), RIGHT(90f) }
80+
81+
@Composable
82+
fun <T : Any> Layout(
83+
tabs: List<T>,
84+
position: Position,
85+
labelFn: (T) -> String,
86+
iconFn: (T) -> Icon.Code,
87+
isActiveFn: (T) -> Boolean,
88+
onClick: (T) -> Unit,
89+
) {
90+
Column(
91+
modifier = Modifier.width(WIDTH).background(Theme.studio.backgroundMedium),
92+
verticalArrangement = Arrangement.Top
93+
) { tabs.forEach { Tab(it, position, labelFn, iconFn, isActiveFn, onClick) } }
94+
}
95+
96+
@OptIn(ExperimentalComposeUiApi::class)
97+
@Composable
98+
private fun <T : Any> Tab(
99+
tab: T,
100+
position: Position,
101+
labelFn: (T) -> String,
102+
iconFn: (T) -> Icon.Code,
103+
isActiveFn: (T) -> Boolean,
104+
onClick: (T) -> Unit,
105+
) {
106+
@Composable
107+
fun bgColor(): Color = if (isActiveFn(tab)) Theme.studio.surface else Theme.studio.backgroundDark
108+
Box(
109+
modifier = Modifier
110+
.fillMaxWidth()
111+
.height(TAB_HEIGHT)
112+
.pointerHoverIcon(PointerIconDefaults.Hand)
113+
.clickable { onClick(tab) }
114+
) {
115+
Row(
116+
verticalAlignment = Alignment.CenterVertically,
117+
modifier = Modifier.requiredWidth(TAB_HEIGHT)
118+
.rotate(position.degree)
119+
.offset(x = TAB_OFFSET)
120+
.background(color = bgColor())
121+
) {
122+
Spacer(modifier = Modifier.weight(1f))
123+
Icon.Render(icon = iconFn(tab), size = TAB_ICON_SIZE)
124+
Spacer(modifier = Modifier.width(TAB_SPACING))
125+
Form.Text(value = labelFn(tab))
126+
Spacer(modifier = Modifier.weight(1f))
127+
}
128+
}
129+
Separator.Horizontal()
130+
}
131+
}
68132

69133
object Horizontal {
70134

135+
private val HEIGHT = Theme.PANEL_BAR_HEIGHT
136+
71137
enum class Position { TOP, BOTTOM }
72138

73139
class State<T : Any> constructor(private val coroutineScope: CoroutineScope) {
@@ -118,15 +184,15 @@ object Tabs {
118184
state.density = LocalDensity.current.density
119185
val closedTabs = state.openedTabSize.keys - tabs.toSet()
120186
closedTabs.forEach { state.openedTabSize.remove(it) }
121-
Row(Modifier.fillMaxWidth().height(PANEL_BAR_HEIGHT).onSizeChanged {
122-
state.maxWidth = toDP(it.width, state.density) - PANEL_BAR_HEIGHT * 3
187+
Row(Modifier.fillMaxWidth().height(HEIGHT).onSizeChanged {
188+
state.maxWidth = toDP(it.width, state.density) - HEIGHT * 3
123189
}) {
124190
if (tabs.isNotEmpty()) Separator.Vertical()
125191
if (state.scroller.maxValue > 0) {
126192
PreviousTabsButton(state)
127193
Separator.Vertical()
128194
}
129-
Row(Modifier.widthIn(max = state.maxWidth).height(PANEL_BAR_HEIGHT).horizontalScroll(state.scroller)) {
195+
Row(Modifier.widthIn(max = state.maxWidth).height(HEIGHT).horizontalScroll(state.scroller)) {
130196
tabs.forEach { tab ->
131197
val icon = iconFn?.let { it(tab) }
132198
val label = labelFn(tab)
@@ -157,7 +223,7 @@ object Tabs {
157223

158224
@Composable
159225
private fun Spacer() {
160-
Spacer(modifier = Modifier.width(PANEL_BAR_SPACING))
226+
Spacer(modifier = Modifier.width(TAB_SPACING))
161227
}
162228

163229
@OptIn(ExperimentalComposeUiApi::class)
@@ -170,7 +236,7 @@ object Tabs {
170236
) {
171237
val contextMenuState = remember { ContextMenu.State() }
172238
val bgColor = if (isActive) Theme.studio.primary else Color.Transparent
173-
val height = if (isActive) PANEL_BAR_HEIGHT - TAB_UNDERLINE_HEIGHT else PANEL_BAR_HEIGHT
239+
val height = if (isActive) HEIGHT - TAB_UNDERLINE_HEIGHT else HEIGHT
174240
var width by remember { mutableStateOf(0.dp) }
175241

176242
Box {
@@ -188,7 +254,7 @@ object Tabs {
188254
trailingButton?.let { Button(it) }
189255
icon?.let {
190256
Spacer()
191-
Icon.Render(icon = it.code, color = it.color(), size = ICON_SIZE)
257+
Icon.Render(icon = it.code, color = it.color(), size = TAB_ICON_SIZE)
192258
Spacer()
193259
}
194260
if (trailingButton == null && icon == null) Spacer()
@@ -220,7 +286,7 @@ object Tabs {
220286
private fun <T : Any> PreviousTabsButton(state: State<T>) {
221287
Form.IconButton(
222288
icon = Icon.Code.CARET_LEFT,
223-
modifier = Modifier.size(PANEL_BAR_HEIGHT),
289+
modifier = Modifier.size(HEIGHT),
224290
bgColor = Color.Transparent,
225291
roundedCorners = Theme.RoundedCorners.NONE,
226292
enabled = state.scroller.value > 0
@@ -231,7 +297,7 @@ object Tabs {
231297
private fun <T : Any> NextTabsButton(state: State<T>) {
232298
Form.IconButton(
233299
icon = Icon.Code.CARET_RIGHT,
234-
modifier = Modifier.size(PANEL_BAR_HEIGHT),
300+
modifier = Modifier.size(HEIGHT),
235301
bgColor = Color.Transparent,
236302
roundedCorners = Theme.RoundedCorners.NONE,
237303
enabled = state.scroller.value < state.scroller.maxValue
@@ -243,7 +309,7 @@ object Tabs {
243309
Form.IconButton(
244310
icon = buttonArg.icon,
245311
hoverIcon = buttonArg.hoverIcon,
246-
modifier = Modifier.size(PANEL_BAR_HEIGHT),
312+
modifier = Modifier.size(HEIGHT),
247313
iconColor = buttonArg.color(),
248314
iconHoverColor = buttonArg.hoverColor?.invoke(),
249315
disabledColor = buttonArg.disabledColor?.invoke(),

0 commit comments

Comments
 (0)