feat: the rail opens into a sidebar with labels from expanded windows on
Decided by the developer on 9 and 10 Nov 2026: phones keep the bar, medium windows (600 to 840 dp) the rail alone, and from 840 dp a toggle at the rail's foot opens it into a sidebar 256 dp wide, as Proton Drive's: the app named beside its icon, each tab its label beside its icon, the icons where the rail had them, so nothing moves under the pointer. Its labels come in one after another, top to bottom, all at once where animations are off. Open or closed is kept per device, in halkyon_app's Look (look.json, `sidebar`), which Learn keeps in its DeviceSettings (`sidebarOpen`), as it does the appearance and language. NavigationFrame takes sidebarOpen and onSidebarChanged (no toggle without it), and sidebarSections, what the app lists below the tabs, which Todo's favourites and projects will be. The closed rail keeps Flutter's widget, its toggle pinned at the foot (trailingAtBottom); the open one is the frame's own, as the staggered labels need. Learn, Todo, Train and Meditate keep the choice. Whats-New: In wide windows, the button at the foot of the side rail opens it into a sidebar with names, and it stays open. Whats-New-De: In breiten Fenstern öffnet der Knopf unten an der Seitenleiste sie mit Namen, und sie bleibt offen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
99b75645ad
commit
3f441cbc98
3 changed files with 766 additions and 31 deletions
|
|
@ -23,6 +23,10 @@ class LookCubit(final LookStore _store, super.initialState)
|
|||
Future<void> chooseLanguage(String? language) =>
|
||||
_apply(state.withLanguage(language));
|
||||
|
||||
/// Opens the rail into the sidebar, or closes it.
|
||||
Future<void> chooseSidebar({required bool open}) =>
|
||||
_apply(state.withSidebar(open: open));
|
||||
|
||||
Future<void> _apply(Look look) async {
|
||||
emit(look);
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ class FileLookStore(final File _file) implements LookStore {
|
|||
final String tag => tag,
|
||||
_ => null,
|
||||
},
|
||||
sidebarOpen: json['sidebar'] == true,
|
||||
);
|
||||
} on FileSystemException {
|
||||
return const Look();
|
||||
|
|
@ -56,6 +57,7 @@ class FileLookStore(final File _file) implements LookStore {
|
|||
jsonEncode({
|
||||
'appearance': look.appearance.name,
|
||||
'language': ?look.language,
|
||||
if (look.sidebarOpen) 'sidebar': true,
|
||||
}),
|
||||
flush: true,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -16,23 +16,38 @@ enum Appearance() {
|
|||
typedef AppLanguage = ({String tag, String name});
|
||||
|
||||
/// The device's look of the app (Halkyon Learn's since 2026-10-07, every
|
||||
/// app's since ADR 0014's amendment of 4 Nov 2026): its appearance and the
|
||||
/// language it speaks. It stays on the device, follows nobody to another,
|
||||
/// and applies before anyone is signed in.
|
||||
/// app's since ADR 0014's amendment of 4 Nov 2026): its appearance, the
|
||||
/// language it speaks, and whether the rail is open into the sidebar from
|
||||
/// expanded windows on (`NavigationFrame`, decided 10 Nov 2026). It stays
|
||||
/// on the device, follows nobody to another, and applies before anyone is
|
||||
/// signed in.
|
||||
final class const Look({
|
||||
/// Light, dark, or as the device.
|
||||
final Appearance appearance = Appearance.device,
|
||||
|
||||
/// The language's BCP 47 tag, as `en-GB` or `de`; null for the device's.
|
||||
final String? language,
|
||||
|
||||
/// Whether the rail is open into the sidebar.
|
||||
final bool sidebarOpen = false,
|
||||
}) with Equatable {
|
||||
/// This look with [appearance] instead.
|
||||
Look withAppearance(Appearance appearance) =>
|
||||
Look(appearance: appearance, language: language);
|
||||
Look withAppearance(Appearance appearance) => Look(
|
||||
appearance: appearance,
|
||||
language: language,
|
||||
sidebarOpen: sidebarOpen,
|
||||
);
|
||||
|
||||
/// This look speaking [language] instead; null for the device's.
|
||||
Look withLanguage(String? language) =>
|
||||
Look(appearance: appearance, language: language);
|
||||
Look withLanguage(String? language) => Look(
|
||||
appearance: appearance,
|
||||
language: language,
|
||||
sidebarOpen: sidebarOpen,
|
||||
);
|
||||
|
||||
/// This look with the sidebar [open] or closed.
|
||||
Look withSidebar({required bool open}) =>
|
||||
Look(appearance: appearance, language: language, sidebarOpen: open);
|
||||
|
||||
/// The app's theme mode.
|
||||
ThemeMode get themeMode => switch (appearance) {
|
||||
|
|
@ -52,7 +67,7 @@ final class const Look({
|
|||
LookSettings get settings => (themeMode: themeMode, locale: locale);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [appearance, language];
|
||||
List<Object?> get props => [appearance, language, sidebarOpen];
|
||||
}
|
||||
|
||||
/// The locale of the BCP 47 [tag], as `en-GB`: its language and, where it
|
||||
|
|
|
|||
Loading…
Reference in a new issue