2
2
3
3
package com.jetpackduba.gitnuro.ui
4
4
5
+ import androidx.compose.desktop.ui.tooling.preview.Preview
5
6
import androidx.compose.foundation.ExperimentalFoundationApi
6
7
import androidx.compose.foundation.Image
7
8
import androidx.compose.foundation.background
@@ -20,10 +21,12 @@ import androidx.compose.ui.draw.clip
20
21
import androidx.compose.ui.focus.FocusRequester
21
22
import androidx.compose.ui.graphics.ColorFilter
22
23
import androidx.compose.ui.graphics.painter.Painter
24
+ import androidx.compose.ui.input.key.Key
23
25
import androidx.compose.ui.layout.LayoutCoordinates
24
26
import androidx.compose.ui.layout.boundsInRoot
25
27
import androidx.compose.ui.layout.onGloballyPositioned
26
28
import androidx.compose.ui.res.painterResource
29
+ import androidx.compose.ui.text.font.FontWeight
27
30
import androidx.compose.ui.text.style.TextAlign
28
31
import androidx.compose.ui.unit.*
29
32
import androidx.compose.ui.window.Popup
@@ -34,6 +37,11 @@ import com.jetpackduba.gitnuro.extensions.handMouseClickable
34
37
import com.jetpackduba.gitnuro.extensions.handOnHover
35
38
import com.jetpackduba.gitnuro.extensions.ignoreKeyEvents
36
39
import com.jetpackduba.gitnuro.git.remote_operations.PullType
40
+ import com.jetpackduba.gitnuro.keybindings.Keybinding
41
+ import com.jetpackduba.gitnuro.keybindings.KeybindingOption
42
+ import com.jetpackduba.gitnuro.keybindings.keyBinding
43
+ import com.jetpackduba.gitnuro.theme.notoSansMonoFontFamily
44
+ import com.jetpackduba.gitnuro.theme.onBackgroundSecondary
37
45
import com.jetpackduba.gitnuro.ui.components.PrimaryButton
38
46
import com.jetpackduba.gitnuro.ui.components.tooltip.InstantTooltip
39
47
import com.jetpackduba.gitnuro.ui.context_menu.*
@@ -62,19 +70,17 @@ fun Menu(
62
70
horizontalArrangement = Arrangement .Center ,
63
71
verticalAlignment = Alignment .CenterVertically ,
64
72
) {
65
- InstantTooltip (
66
- text = " Open a different repository" ,
67
- enabled = ! showOpenPopup,
68
- ) {
69
- MenuButton (
70
- modifier = Modifier
71
- .padding(start = 16 .dp)
72
- .onGloballyPositioned { setPosition(it) },
73
- title = " Open" ,
74
- icon = painterResource(AppIcons .OPEN ),
75
- onClick = { onShowOpenPopupChange(true ) },
76
- )
77
- }
73
+ MenuButton (
74
+ modifier = Modifier
75
+ .padding(start = 16 .dp)
76
+ .onGloballyPositioned { setPosition(it) },
77
+ title = " Open" ,
78
+ icon = painterResource(AppIcons .OPEN ),
79
+ keybinding = KeybindingOption .OPEN_REPOSITORY .keyBinding,
80
+ tooltip = " Open a different repository" ,
81
+ tooltipEnabled = ! showOpenPopup,
82
+ onClick = { onShowOpenPopupChange(true ) },
83
+ )
78
84
79
85
Spacer (modifier = Modifier .weight(1f ))
80
86
@@ -126,16 +132,15 @@ fun Menu(
126
132
127
133
Spacer (modifier = Modifier .width(32 .dp))
128
134
129
- InstantTooltip (
130
- text = " Create a new branch" ,
131
- ) {
132
- MenuButton (
133
- title = " Branch" ,
134
- icon = painterResource(AppIcons .BRANCH ),
135
- ) {
135
+ MenuButton (
136
+ title = " Branch" ,
137
+ icon = painterResource(AppIcons .BRANCH ),
138
+ onClick = {
136
139
onCreateBranch()
137
- }
138
- }
140
+ },
141
+ tooltip = " Create a new branch" ,
142
+ keybinding = KeybindingOption .BRANCH_CREATE .keyBinding,
143
+ )
139
144
140
145
141
146
Spacer (modifier = Modifier .width(32 .dp))
@@ -151,43 +156,42 @@ fun Menu(
151
156
)
152
157
)
153
158
154
- InstantTooltip (
155
- text = " Pop the last stash"
156
- ) {
157
- MenuButton (
158
- title = " Pop" ,
159
- icon = painterResource(AppIcons .APPLY_STASH ),
160
- ) { menuViewModel.popStash() }
161
- }
159
+ MenuButton (
160
+ title = " Pop" ,
161
+ icon = painterResource(AppIcons .APPLY_STASH ),
162
+ keybinding = KeybindingOption .STASH_POP .keyBinding,
163
+ tooltip = " Pop the last stash" ,
164
+ ) { menuViewModel.popStash() }
162
165
163
166
Spacer (modifier = Modifier .weight(1f ))
164
167
165
- InstantTooltip (
166
- text = " Open a terminal in the repository's path"
167
- ) {
168
- MenuButton (
169
- modifier = Modifier .padding(end = 4 .dp),
170
- title = " Terminal" ,
171
- icon = painterResource(AppIcons .TERMINAL ),
172
- onClick = { menuViewModel.openTerminal() },
173
- )
174
- }
168
+ MenuButton (
169
+ modifier = Modifier .padding(end = 4 .dp),
170
+ title = " Terminal" ,
171
+ icon = painterResource(AppIcons .TERMINAL ),
172
+ onClick = { menuViewModel.openTerminal() },
173
+ tooltip = " Open a terminal in the repository's path" ,
174
+ keybinding = null ,
175
+ )
175
176
176
177
MenuButton (
177
178
modifier = Modifier .padding(end = 4 .dp),
178
179
title = " Actions" ,
179
180
icon = painterResource(AppIcons .BOLT ),
180
181
onClick = onQuickActions,
182
+ tooltip = " Additional actions" ,
183
+ keybinding = null ,
181
184
)
182
185
183
- InstantTooltip (
184
- text = " Gitnuro's settings" ,
186
+ Box (
185
187
modifier = Modifier .padding(end = 16 .dp)
186
188
) {
187
189
MenuButton (
188
190
title = " Settings" ,
189
191
icon = painterResource(AppIcons .SETTINGS ),
190
192
onClick = onShowSettingsDialog,
193
+ tooltip = " Gitnuro's settings" ,
194
+ keybinding = KeybindingOption .STASH_POP .keyBinding,
191
195
)
192
196
}
193
197
}
@@ -257,33 +261,135 @@ fun MenuButton(
257
261
enabled : Boolean = true,
258
262
title : String ,
259
263
icon : Painter ,
260
- onClick : () -> Unit
264
+ keybinding : Keybinding ? ,
265
+ tooltip : String ,
266
+ tooltipEnabled : Boolean = true,
267
+ onClick : () -> Unit ,
261
268
) {
262
- Column (
263
- modifier = modifier
264
- .ignoreKeyEvents()
265
- .clip(RoundedCornerShape (4 .dp))
266
- .background(MaterialTheme .colors.surface)
267
- .handMouseClickable { if (enabled) onClick() }
268
- .size(56 .dp),
269
- horizontalAlignment = Alignment .CenterHorizontally ,
270
- verticalArrangement = Arrangement .Center ,
269
+ InstantTooltip (
270
+ text = tooltip,
271
+ enabled = tooltipEnabled,
272
+ trailingContent = if (keybinding != null ) {
273
+ { KeybindingHint (keybinding) }
274
+ } else {
275
+ null
276
+ }
271
277
) {
272
- Icon (
273
- painter = icon,
274
- contentDescription = title,
275
- modifier = Modifier
276
- .size(24 .dp),
277
- tint = MaterialTheme .colors.onBackground,
278
- )
279
- Text (
280
- text = title,
281
- style = MaterialTheme .typography.caption,
282
- maxLines = 1 ,
283
- textAlign = TextAlign .Center ,
284
- color = MaterialTheme .colors.onBackground,
285
- )
278
+ Column (
279
+ modifier = modifier
280
+ .ignoreKeyEvents()
281
+ .clip(RoundedCornerShape (4 .dp))
282
+ .background(MaterialTheme .colors.surface)
283
+ .handMouseClickable { if (enabled) onClick() }
284
+ .size(56 .dp),
285
+ horizontalAlignment = Alignment .CenterHorizontally ,
286
+ verticalArrangement = Arrangement .Center ,
287
+ ) {
288
+ Icon (
289
+ painter = icon,
290
+ contentDescription = title,
291
+ modifier = Modifier
292
+ .size(24 .dp),
293
+ tint = MaterialTheme .colors.onBackground,
294
+ )
295
+ Text (
296
+ text = title,
297
+ style = MaterialTheme .typography.caption,
298
+ maxLines = 1 ,
299
+ textAlign = TextAlign .Center ,
300
+ color = MaterialTheme .colors.onBackground,
301
+ )
302
+ }
303
+ }
304
+ }
305
+
306
+ @Composable
307
+ fun KeybindingHint (keybinding : Keybinding ) {
308
+ val parts = remember(keybinding) { getParts(keybinding) }.joinToString(" +" )
309
+
310
+ Text (
311
+ parts,
312
+ fontFamily = notoSansMonoFontFamily,
313
+ fontSize = MaterialTheme .typography.caption.fontSize,
314
+ fontWeight = FontWeight .Medium ,
315
+ color = MaterialTheme .colors.onBackgroundSecondary,
316
+ )
317
+ }
318
+
319
+ @Preview
320
+ @Composable
321
+ fun KeybindingHintPartPreview () {
322
+ KeybindingHintPart (" CTRL" )
323
+ }
324
+
325
+ @Composable
326
+ fun KeybindingHintPart (part : String ) {
327
+ Text (
328
+ text = part,
329
+ fontWeight = FontWeight .Medium ,
330
+ color = MaterialTheme .colors.primary,
331
+ modifier = Modifier
332
+ .clip(RoundedCornerShape (4 .dp))
333
+ .border(2 .dp, MaterialTheme .colors.primary, RoundedCornerShape (4 .dp))
334
+ .background(MaterialTheme .colors.primary.copy(alpha = 0.05f ))
335
+ .padding(horizontal = 4 .dp, vertical = 4 .dp)
336
+
337
+ )
338
+ }
339
+
340
+ fun getParts (keybinding : Keybinding ): List <String > {
341
+ val parts = mutableListOf<String >()
342
+
343
+ if (keybinding.control) {
344
+ parts.add(" Ctrl" )
286
345
}
346
+
347
+ if (keybinding.meta) {
348
+ parts.add(" ⌘" )
349
+ }
350
+
351
+ if (keybinding.alt) {
352
+ parts.add(" Alt" )
353
+ }
354
+
355
+ if (keybinding.shift) {
356
+ parts.add(" Shift" )
357
+ }
358
+
359
+ val key = when (keybinding.key) {
360
+ Key .A -> " A"
361
+ Key .B -> " B"
362
+ Key .C -> " C"
363
+ Key .D -> " D"
364
+ Key .E -> " E"
365
+ Key .F -> " F"
366
+ Key .G -> " G"
367
+ Key .H -> " H"
368
+ Key .I -> " I"
369
+ Key .J -> " J"
370
+ Key .K -> " K"
371
+ Key .L -> " L"
372
+ Key .M -> " M"
373
+ Key .N -> " N"
374
+ Key .O -> " O"
375
+ Key .P -> " P"
376
+ Key .Q -> " Q"
377
+ Key .R -> " R"
378
+ Key .S -> " S"
379
+ Key .T -> " T"
380
+ Key .U -> " U"
381
+ Key .V -> " V"
382
+ Key .W -> " W"
383
+ Key .X -> " X"
384
+ Key .Y -> " Y"
385
+ Key .Z -> " Z"
386
+ Key .Tab -> " Tab"
387
+ else -> throw NotImplementedError (" Key not implemented" )
388
+ }
389
+
390
+ parts.add(key)
391
+
392
+ return parts
287
393
}
288
394
289
395
@Composable
0 commit comments