TASK-061: edge tab for the side pane, and a new app icon
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
023fa0b728
commit
17fa248065
@ -147,6 +147,7 @@ private enum class ViewerLayout {
|
||||
private val CompactHeightBreakpoint = 480.dp
|
||||
private val SidePaneWidth = 288.dp
|
||||
private val TreeIndent = 14.dp
|
||||
private val EdgeTabTouchWidth = 28.dp
|
||||
private val NarrowSidePaneWidth = 220.dp
|
||||
private val NarrowWideBreakpoint = 700.dp
|
||||
|
||||
@ -376,8 +377,9 @@ fun CodeViewerScreen(
|
||||
LocationStrip(enclosingSymbols(document.symbols, firstVisibleLine))
|
||||
}
|
||||
|
||||
Box(Modifier.weight(1f).fillMaxWidth()) {
|
||||
Row(
|
||||
Modifier.weight(1f).fillMaxWidth().openFromLeftEdge(enabled = !sidePaneIsShown && drawerState.isClosed) {
|
||||
Modifier.fillMaxSize().openFromLeftEdge(enabled = !sidePaneIsShown && drawerState.isClosed) {
|
||||
scope.launch { drawerState.open() }
|
||||
},
|
||||
) {
|
||||
@ -448,6 +450,12 @@ fun CodeViewerScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
// Says that there is something to draw out from this edge; without it nothing on the
|
||||
// screen does.
|
||||
if (!sidePaneIsShown) {
|
||||
EdgeTab(Modifier.align(Alignment.CenterStart)) { scope.launch { drawerState.open() } }
|
||||
}
|
||||
}
|
||||
if (layout != ViewerLayout.LANDSCAPE) Toolbar(tools)
|
||||
}
|
||||
}
|
||||
@ -835,6 +843,34 @@ private fun JumpToCounterpart(counterpart: RepositoryPath, modifier: Modifier =
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A small tab on the left edge, where the side pane is drawn out from. It can be tapped as well:
|
||||
* a swipe from the edge is not available to everyone.
|
||||
*/
|
||||
@Composable
|
||||
private fun EdgeTab(modifier: Modifier = Modifier, onOpen: () -> Unit) {
|
||||
val description = stringResource(R.string.codeviewer_edge_tab)
|
||||
Box(
|
||||
modifier
|
||||
.size(width = EdgeTabTouchWidth, height = 72.dp)
|
||||
.clickable(role = Role.Button, onClickLabel = description, onClick = onOpen)
|
||||
.semantics { contentDescription = description },
|
||||
contentAlignment = Alignment.CenterStart,
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.size(width = 14.dp, height = 56.dp)
|
||||
.background(
|
||||
MaterialTheme.colorScheme.primary.copy(alpha = 0.85f),
|
||||
RoundedCornerShape(topEnd = 12.dp, bottomEnd = 12.dp),
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
UBIcon(R.drawable.ic_ub_chevron_right, null, size = 14.dp, tint = MaterialTheme.colorScheme.onPrimary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A header and its source, side by side, header on the left whichever of the two was opened. The
|
||||
* bar between them is dragged to give one side more of the width.
|
||||
|
||||
@ -1,14 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- App icon background layer: a solid fill in the same sky blue used as this app's Material 3
|
||||
primary color (ui/theme/Color.kt's SkyPrimaryLight), so the home-screen icon and the in-app
|
||||
theme read as the same deliberate color rather than drifting apart after the sky-blue redesign
|
||||
(this used to match the earlier "Gitea green" theme's primary before that changed). -->
|
||||
<!-- App icon, back layer (TASK-061): the bright sky blue of the app (BrandSky in ui/theme/Color.kt) running to its deeper primary, with two faint circles so the flat color has some depth -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M0,0h108v108h-108z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:type="linear"
|
||||
android:startX="14"
|
||||
android:startY="8"
|
||||
android:endX="94"
|
||||
android:endY="100">
|
||||
<item android:offset="0" android:color="#FF4DB0FF" />
|
||||
<item android:offset="0.55" android:color="#FF2F98F9" />
|
||||
<item android:offset="1" android:color="#FF0A6FD8" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#0369A1"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
android:fillColor="#1FFFFFFF"
|
||||
android:pathData="M58,88 a34,34 0 1,0 68,0 a34,34 0 1,0 -68,0 Z" />
|
||||
<path
|
||||
android:fillColor="#14FFFFFF"
|
||||
android:pathData="M-14,16 a26,26 0 1,0 52,0 a26,26 0 1,0 -52,0 Z" />
|
||||
</vector>
|
||||
|
||||
@ -1,35 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- A simple git-branch glyph (trunk node, a straight line up to a main-branch node, a curved line
|
||||
out to a second branch node), reading immediately as "a git tool" without reproducing Gitea's
|
||||
own trademarked logo. Every coordinate stays within the adaptive-icon safe zone (the centered
|
||||
66dp-diameter circle on this 108dp canvas), since the system mask crops outside it on some
|
||||
launchers. This app has no icon library (see CLAUDE.md's established convention), so this is
|
||||
hand-drawn geometry, circles for nodes and stroked paths for the connecting branch lines,
|
||||
matching the same plain-vector approach already used for the notification small icon. -->
|
||||
<!-- App icon, front layer (TASK-061): a branch that leaves the trunk, its commits drawn as rings. It says "Git" without using Gitea's own logo. Everything stays inside the 66dp safe circle of the 108dp canvas, which is all that some launchers show -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<!-- Drawn large, then brought inside the safe circle. -->
|
||||
<group
|
||||
android:pivotX="54"
|
||||
android:pivotY="54"
|
||||
android:scaleX="0.85"
|
||||
android:scaleY="0.85"
|
||||
android:translateX="-2">
|
||||
<path
|
||||
android:pathData="M40,78 L40,30"
|
||||
android:pathData="M41,74 L41,34"
|
||||
android:strokeColor="#FFFFFFFF"
|
||||
android:strokeWidth="6"
|
||||
android:strokeWidth="6.5"
|
||||
android:strokeLineCap="round"
|
||||
android:fillColor="#00000000" />
|
||||
<path
|
||||
android:pathData="M40,78 C40,60 74,64 74,46"
|
||||
android:pathData="M41,74 C41,57 72,61 72,45"
|
||||
android:strokeColor="#FFFFFFFF"
|
||||
android:strokeWidth="6"
|
||||
android:strokeWidth="6.5"
|
||||
android:strokeLineCap="round"
|
||||
android:fillColor="#00000000" />
|
||||
<!-- Each node is a ring: the outer circle with the inner one cut out of it. -->
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M33,78 A7,7 0 1,0 47,78 A7,7 0 1,0 33,78 Z" />
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M31.5,76 a9.5,9.5 0 1,0 19,0 a9.5,9.5 0 1,0 -19,0 Z M36.5,76 a4.5,4.5 0 1,0 9,0 a4.5,4.5 0 1,0 -9,0 Z" />
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M33,30 A7,7 0 1,0 47,30 A7,7 0 1,0 33,30 Z" />
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M31.5,32 a9.5,9.5 0 1,0 19,0 a9.5,9.5 0 1,0 -19,0 Z M36.5,32 a4.5,4.5 0 1,0 9,0 a4.5,4.5 0 1,0 -9,0 Z" />
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M67,46 A7,7 0 1,0 81,46 A7,7 0 1,0 67,46 Z" />
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M62.5,43 a9.5,9.5 0 1,0 19,0 a9.5,9.5 0 1,0 -19,0 Z M67.5,43 a4.5,4.5 0 1,0 9,0 a4.5,4.5 0 1,0 -9,0 Z" />
|
||||
</group>
|
||||
</vector>
|
||||
|
||||
@ -1,31 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Android 13+ themed-icon layer. The system re-tints this to match the device wallpaper theme,
|
||||
so (like the notification small icon) only the shape/alpha matters, not this fill color.
|
||||
Same glyph as ic_launcher_foreground.xml. -->
|
||||
<!-- App icon, themed layer (Android 13+): the same shape as the front layer. The system colors it, so only the shape matters -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<!-- Drawn large, then brought inside the safe circle. -->
|
||||
<group
|
||||
android:pivotX="54"
|
||||
android:pivotY="54"
|
||||
android:scaleX="0.85"
|
||||
android:scaleY="0.85"
|
||||
android:translateX="-2">
|
||||
<path
|
||||
android:pathData="M40,78 L40,30"
|
||||
android:pathData="M41,74 L41,34"
|
||||
android:strokeColor="#FF000000"
|
||||
android:strokeWidth="6"
|
||||
android:strokeWidth="6.5"
|
||||
android:strokeLineCap="round"
|
||||
android:fillColor="#00000000" />
|
||||
<path
|
||||
android:pathData="M40,78 C40,60 74,64 74,46"
|
||||
android:pathData="M41,74 C41,57 72,61 72,45"
|
||||
android:strokeColor="#FF000000"
|
||||
android:strokeWidth="6"
|
||||
android:strokeWidth="6.5"
|
||||
android:strokeLineCap="round"
|
||||
android:fillColor="#00000000" />
|
||||
<!-- Each node is a ring: the outer circle with the inner one cut out of it. -->
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M33,78 A7,7 0 1,0 47,78 A7,7 0 1,0 33,78 Z" />
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M31.5,76 a9.5,9.5 0 1,0 19,0 a9.5,9.5 0 1,0 -19,0 Z M36.5,76 a4.5,4.5 0 1,0 9,0 a4.5,4.5 0 1,0 -9,0 Z" />
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M33,30 A7,7 0 1,0 47,30 A7,7 0 1,0 33,30 Z" />
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M31.5,32 a9.5,9.5 0 1,0 19,0 a9.5,9.5 0 1,0 -19,0 Z M36.5,32 a4.5,4.5 0 1,0 9,0 a4.5,4.5 0 1,0 -9,0 Z" />
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M67,46 A7,7 0 1,0 81,46 A7,7 0 1,0 67,46 Z" />
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M62.5,43 a9.5,9.5 0 1,0 19,0 a9.5,9.5 0 1,0 -19,0 Z M67.5,43 a4.5,4.5 0 1,0 9,0 a4.5,4.5 0 1,0 -9,0 Z" />
|
||||
</group>
|
||||
</vector>
|
||||
|
||||
@ -69,6 +69,7 @@
|
||||
<string name="codeviewer_files_title">폴더 구조</string>
|
||||
<string name="codeviewer_symbols_title">클래스 · 함수</string>
|
||||
<string name="codeviewer_symbols_empty">이 파일에는 표시할 클래스나 함수가 없습니다.</string>
|
||||
<string name="codeviewer_edge_tab">폴더 구조와 클래스 · 함수 열기</string>
|
||||
<string name="codeviewer_pane_swap">위아래 바꾸기</string>
|
||||
<string name="codeviewer_folder_open">펼쳐짐</string>
|
||||
<string name="codeviewer_folder_closed">접힘</string>
|
||||
|
||||
Binary file not shown.
@ -1410,3 +1410,13 @@ This is the append-only implementation and handoff record shared by Codex and Cl
|
||||
- Checks and results: the required checks pass; 437 unit tests, 0 failures; lint 0 errors.
|
||||
- Live verification: installed on the SM-F966N; not seen on a screen by the implementer.
|
||||
- Next owner: the product owner, to confirm on the device.
|
||||
|
||||
## 2026-09-28 (continued) — TASK-061: a tab on the left edge, and a new app icon
|
||||
|
||||
- Request: the human product owner asked for a mark showing that the side pane can be drawn out
|
||||
from the left edge of the code, and for a more fitting app icon.
|
||||
- What changed: see `tasks/061-edge-tab-and-app-icon.md`.
|
||||
- Checks and results: the required checks pass; 437 unit tests, 0 failures; lint 0 errors.
|
||||
- Live verification: installed on the SM-F966N; neither change was seen on the device by the
|
||||
implementer.
|
||||
- Next owner: the product owner, to confirm on the device.
|
||||
|
||||
60
tasks/061-edge-tab-and-app-icon.md
Normal file
60
tasks/061-edge-tab-and-app-icon.md
Normal file
@ -0,0 +1,60 @@
|
||||
# TASK-061: A tab on the left edge, and a new app icon
|
||||
|
||||
Status: implemented; required checks pass; installed on the product owner's Z Fold7, awaiting their confirmation
|
||||
Owner: Claude
|
||||
Reviewer: human product owner
|
||||
|
||||
## Outcome
|
||||
|
||||
Requested by the human product owner, 2026-09-28: nothing on the code screen said that the side
|
||||
pane could be drawn out from the left edge, and the app icon should be changed to suit the app.
|
||||
|
||||
## Scope
|
||||
|
||||
- Included:
|
||||
1. A small tab at the middle of the code's left edge, with an arrow. It marks where the pane is
|
||||
drawn out from, and tapping it opens the pane as well. It is not shown where the pane is
|
||||
already beside the code.
|
||||
2. App icon: the background is the app's bright sky blue running to its deeper primary (it was
|
||||
a flat darker blue from before the redesign); the branch glyph's commits are drawn as rings.
|
||||
The themed (monochrome) layer has the same shape.
|
||||
- Excluded: using the UbiSam logo in the icon. It is a wordmark, and unreadable at icon size.
|
||||
|
||||
## Writable paths
|
||||
|
||||
- `app/src/main/java/com/ubisam/ubgitea/feature/codebrowser/CodeViewerScreen.kt`
|
||||
- `app/src/main/res/drawable/ic_launcher_*.xml`
|
||||
- `app/src/main/res/values/strings_codebrowser.xml`
|
||||
- `tasks/`, `docs/`
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [x] The tab has a spoken description and a touch area wider than what is drawn.
|
||||
- [x] The icon's glyph stays inside the 66dp safe circle of the adaptive icon.
|
||||
|
||||
## Security and privacy
|
||||
|
||||
No additional concerns.
|
||||
|
||||
## Required checks
|
||||
|
||||
```text
|
||||
./gradlew compileDebugKotlin compileDebugUnitTestKotlin testDebugUnitTest assembleDebug lintDebug
|
||||
```
|
||||
|
||||
## Evidence
|
||||
|
||||
- Required checks pass: 437 unit tests, 0 failures; lint 0 errors.
|
||||
- Installed on the SM-F966N over wireless `adb`.
|
||||
- The icon was looked at only as a rough drawing made on a PC from the same coordinates, before
|
||||
the glyph was made smaller to fit the safe circle; not on a launcher. The tab has not been seen
|
||||
on a screen by the implementer.
|
||||
|
||||
## Known limitations
|
||||
|
||||
- The tab covers a strip 14dp wide and 56dp tall of the line numbers behind it.
|
||||
- A launcher may keep showing the old icon until it refreshes its cache.
|
||||
|
||||
## Review findings
|
||||
|
||||
None yet.
|
||||
Loading…
x
Reference in New Issue
Block a user