Restore directory navigation on Android back
This commit is contained in:
1 parent
59965d314f
commit
22f263ccce
2 files changed
+45
-25
No files matched your search
+7
-6
@@ -208,17 +208,18 @@ absence the signal. Back with unsaved changes asks, and says the edits will
|
|||||||
be lost. The explorer draws over the session, which deliberately has no
|
be lost. The explorer draws over the session, which deliberately has no
|
||||||
`imePadding`, so the explorer's own box adds it.
|
`imePadding`, so the explorer's own box adds it.
|
||||||
|
|
||||||
### 10. The explorer draws over the session, and back closes it first
|
### 10. The explorer draws over the session, and the two back controls differ
|
||||||
|
|
||||||
`Screen.Session` in `AppRoot` gains a `files: FilesTarget?`. When set, the
|
`Screen.Session` in `AppRoot` gains a `files: FilesTarget?`. When set, the
|
||||||
`FilesScreen` is composed **on top of** the session in the same `Box`, and
|
`FilesScreen` is composed **on top of** the session in the same `Box`, and
|
||||||
the session stays composed under it: its event stream keeps flowing, its
|
the session stays composed under it: its event stream keeps flowing, its
|
||||||
scroll position and draft stay where they were, and returning from a file
|
scroll position and draft stay where they were, and returning from a file
|
||||||
costs nothing. Back — the button and the platform gesture — returns from an
|
costs nothing. The header's back button clears `files` and returns directly to
|
||||||
open file to its containing directory, then clears `files` and returns to the
|
the session. Android back returns from an open file to its containing directory,
|
||||||
session. Directory navigation stays in the listing: `..` is an explicit row
|
then walks to the parent directory until the root; at the root it returns to the
|
||||||
rather than a hidden second meaning for Back. An editor with unsaved changes
|
session. The `..` row remains as the visible, tappable form of the same directory
|
||||||
asks before returning to the directory. "Back returns; it does not exit."
|
movement. An editor with unsaved changes asks before either route discards them.
|
||||||
|
"Back returns; it does not exit."
|
||||||
|
|
||||||
Rejected: a `Screen.Files` beside `Screen.Session`. Every route back from a
|
Rejected: a `Screen.Files` beside `Screen.Session`. Every route back from a
|
||||||
leaf screen goes to Main today, and a session disposed and re-created on each
|
leaf screen goes to Main today, and a session disposed and re-created on each
|
||||||
|
|||||||
@@ -72,13 +72,18 @@ private sealed class Spot(val path: String) {
|
|||||||
class Doc(path: String, val directory: Dir) : Spot(path)
|
class Doc(path: String, val directory: Dir) : Spot(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum class UnsavedDestination {
|
||||||
|
Directory,
|
||||||
|
Session,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The files on the machine a session runs on: browse them, read one, change one.
|
* The files on the machine a session runs on: browse them, read one, change one.
|
||||||
*
|
*
|
||||||
* Drawn **over** the session rather than instead of it (see [AppRoot]), so its event stream keeps
|
* Drawn **over** the session rather than instead of it (see [AppRoot]), so its event stream keeps
|
||||||
* flowing and coming back from a file costs nothing. Back from a file returns to its directory;
|
* flowing and coming back from a file costs nothing. Android back returns from a file to its
|
||||||
* back from a directory closes the explorer and returns to the session. Directory navigation stays
|
* directory, then walks up the directory tree. The header's back button closes the explorer and
|
||||||
* inside the listing, where its `..` row is explicit.
|
* returns directly to the session.
|
||||||
*
|
*
|
||||||
* Every directory that has been visited is kept for as long as this is open; the refresh glyph is
|
* Every directory that has been visited is kept for as long as this is open; the refresh glyph is
|
||||||
* how one gets asked again on purpose, and creating something refetches the directory it was
|
* how one gets asked again on purpose, and creating something refetches the directory it was
|
||||||
@@ -98,11 +103,10 @@ fun FilesScreen(settings: ServerSettings, target: FilesTarget, onClose: () -> Un
|
|||||||
val listings = remember { mutableStateMapOf<String, LoadState<Listing>>() }
|
val listings = remember { mutableStateMapOf<String, LoadState<Listing>>() }
|
||||||
var creating by remember { mutableStateOf(false) }
|
var creating by remember { mutableStateOf(false) }
|
||||||
// Edit mode and whether anything has been typed live here rather than in the pane below,
|
// Edit mode and whether anything has been typed live here rather than in the pane below,
|
||||||
// because they are what back has to know about -- and back arrives from two places, the arrow
|
// because both ways out have to ask before discarding it.
|
||||||
// and the platform's own gesture, which must mean the same thing.
|
|
||||||
var editing by remember { mutableStateOf(false) }
|
var editing by remember { mutableStateOf(false) }
|
||||||
var dirty by remember { mutableStateOf(false) }
|
var dirty by remember { mutableStateOf(false) }
|
||||||
var askUnsaved by remember { mutableStateOf(false) }
|
var unsavedDestination by remember { mutableStateOf<UnsavedDestination?>(null) }
|
||||||
|
|
||||||
fun go(spot: Spot) {
|
fun go(spot: Spot) {
|
||||||
editing = false
|
editing = false
|
||||||
@@ -110,11 +114,23 @@ fun FilesScreen(settings: ServerSettings, target: FilesTarget, onClose: () -> Un
|
|||||||
here = spot
|
here = spot
|
||||||
}
|
}
|
||||||
|
|
||||||
fun back() {
|
fun leave(destination: UnsavedDestination) {
|
||||||
when {
|
if (editing && dirty) {
|
||||||
editing && dirty -> askUnsaved = true
|
unsavedDestination = destination
|
||||||
here is Spot.Doc -> go((here as Spot.Doc).directory)
|
} else if (destination == UnsavedDestination.Directory) {
|
||||||
else -> onClose()
|
go((here as Spot.Doc).directory)
|
||||||
|
} else {
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun systemBack() {
|
||||||
|
when (val spot = here) {
|
||||||
|
is Spot.Doc -> leave(UnsavedDestination.Directory)
|
||||||
|
is Spot.Dir -> {
|
||||||
|
val path = (listings[spot.path] as? LoadState.Loaded)?.value?.path ?: spot.path
|
||||||
|
parentOf(path)?.let { go(Spot.Dir(it)) } ?: onClose()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +147,7 @@ fun FilesScreen(settings: ServerSettings, target: FilesTarget, onClose: () -> Un
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BackHandler(onBack = ::back)
|
BackHandler(onBack = ::systemBack)
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
Modifier.fillMaxSize()
|
Modifier.fillMaxSize()
|
||||||
@@ -151,7 +167,7 @@ fun FilesScreen(settings: ServerSettings, target: FilesTarget, onClose: () -> Un
|
|||||||
title = baseName(at),
|
title = baseName(at),
|
||||||
path = at,
|
path = at,
|
||||||
machine = target.setupName,
|
machine = target.setupName,
|
||||||
onBack = ::back,
|
onBack = { leave(UnsavedDestination.Session) },
|
||||||
) {
|
) {
|
||||||
GlyphButton(
|
GlyphButton(
|
||||||
REFRESH_GLYPH,
|
REFRESH_GLYPH,
|
||||||
@@ -178,20 +194,23 @@ fun FilesScreen(settings: ServerSettings, target: FilesTarget, onClose: () -> Un
|
|||||||
editing = editing,
|
editing = editing,
|
||||||
onEditing = { editing = it },
|
onEditing = { editing = it },
|
||||||
onDirty = { dirty = it },
|
onDirty = { dirty = it },
|
||||||
onBack = ::back,
|
onBack = { leave(UnsavedDestination.Session) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (askUnsaved) {
|
unsavedDestination?.let { destination ->
|
||||||
UnsavedDialog(
|
UnsavedDialog(
|
||||||
onDiscard = {
|
onDiscard = {
|
||||||
askUnsaved = false
|
unsavedDestination = null
|
||||||
val spot = here
|
if (destination == UnsavedDestination.Directory) {
|
||||||
if (spot is Spot.Doc) go(spot.directory) else onClose()
|
go((here as Spot.Doc).directory)
|
||||||
|
} else {
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onCancel = { askUnsaved = false },
|
onCancel = { unsavedDestination = null },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user