Release 1.0.3: refine tablet layouts, pointer selection and account sync cleanup
This commit is contained in:
+120
-33
@@ -5,9 +5,11 @@ import '../widgets/noo/lists/noo_grouped_list.dart';
|
||||
import '../widgets/noo/lists/noo_settings_row.dart';
|
||||
import '../widgets/noo/nav/noo_top_bar.dart';
|
||||
import '../widgets/noo/nav/noo_toolbar.dart';
|
||||
import '../widgets/noo/nav/noo_sidebar.dart';
|
||||
import '../widgets/noo/noo_layout.dart';
|
||||
import '../widgets/settings/settings_account_card.dart';
|
||||
import '../widgets/settings/settings_accounts.dart';
|
||||
import '../widgets/settings/settings_about.dart';
|
||||
import '../widgets/settings/settings_action_bar.dart';
|
||||
import '../widgets/settings/settings_appearance.dart';
|
||||
import '../widgets/settings/settings_file_sync.dart';
|
||||
@@ -15,35 +17,20 @@ import '../widgets/settings/settings_files_cache.dart';
|
||||
import '../widgets/settings/settings_security.dart';
|
||||
import '../widgets/settings/settings_swipe.dart';
|
||||
import '../widgets/settings/settings_tabs.dart';
|
||||
import '../widgets/settings/settings_section.dart';
|
||||
|
||||
/// Settings, pushed on top of the shell (DESIGN_SYSTEM.md 4's 9-section
|
||||
/// order: account card, accounts, security, file sync, files cache,
|
||||
/// appearance, tabs, action bar, swipe on a file).
|
||||
///
|
||||
/// Every layout (including wide tablet, in a centered 640px column) is
|
||||
/// a two-level menu, the way native iOS/Android Settings apps
|
||||
/// work: [SettingsAccountCard] (the account summary, not a settings picker)
|
||||
/// stays pinned at the top of a single top-level list ([_MobileMenu]), and
|
||||
/// every other section becomes one tappable [NooSettingsRow] - icon, title,
|
||||
/// chevron - in a [NooGroupedList] below it. Tapping a row pushes a new
|
||||
/// [_SettingsCategoryScreen] with its own [NooTopBar]/[NooTopBarBack],
|
||||
/// containing just that section's content full-screen. This replaces an
|
||||
/// earlier design where every section rendered inline in one long
|
||||
/// collapsible-sections column (`_MobileList`, since removed) - and before
|
||||
/// that, a trailing jump rail (an even earlier version) that scrolled that
|
||||
/// *same* page to an anchor. Both were rejected: the jump rail added a
|
||||
/// second, redundant way to navigate on top of plain scrolling without
|
||||
/// shortening the page, and the collapsible-sections column still left a
|
||||
/// long page to scroll past even collapsed. A genuinely separate pushed
|
||||
/// screen per category removes the scroll-depth problem outright, so
|
||||
/// neither a jump rail nor per-section collapsing is needed any more - see
|
||||
/// [SettingsSection]'s doc comment for how that reflects in its mobile
|
||||
/// layout. See each `widgets/settings/*.dart` file for a section's own
|
||||
/// content and any setting that had to be slotted in or grouped under
|
||||
/// "Advanced appearance".
|
||||
class AccountView extends StatelessWidget {
|
||||
/// Settings uses pushed category screens on phones and an inline category
|
||||
/// sidebar with a rounded content pane on tablets.
|
||||
class AccountView extends StatefulWidget {
|
||||
const AccountView({super.key});
|
||||
|
||||
@override
|
||||
State<AccountView> createState() => _AccountViewState();
|
||||
}
|
||||
|
||||
class _AccountViewState extends State<AccountView> {
|
||||
int selected = 0;
|
||||
|
||||
static final _categories = <_SettingsCategory>[
|
||||
_SettingsCategory(
|
||||
title: 'Accounts',
|
||||
@@ -85,6 +72,11 @@ class AccountView extends StatelessWidget {
|
||||
icon: LucideIcons.chevronsLeftRight,
|
||||
builder: (_) => const SettingsSwipeSection(),
|
||||
),
|
||||
_SettingsCategory(
|
||||
title: 'About',
|
||||
icon: LucideIcons.info,
|
||||
builder: (_) => const SettingsAboutSection(),
|
||||
),
|
||||
];
|
||||
|
||||
@override
|
||||
@@ -92,6 +84,101 @@ class AccountView extends StatelessWidget {
|
||||
final colors = context.nooColors;
|
||||
final desktop = NooLayout.isDesktop(context);
|
||||
|
||||
if (desktop) {
|
||||
final category = _categories[selected];
|
||||
final duration = MediaQuery.disableAnimationsOf(context)
|
||||
? Duration.zero
|
||||
: const Duration(milliseconds: 260);
|
||||
return Scaffold(
|
||||
backgroundColor: colors.bg,
|
||||
body: SafeArea(
|
||||
child: Row(
|
||||
children: [
|
||||
NooSidebar(
|
||||
windowControls: Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(LucideIcons.arrowLeft),
|
||||
tooltip: 'Back',
|
||||
onPressed: () => Navigator.maybePop(context),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Settings',
|
||||
style: NooText.title.copyWith(color: colors.fg1),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
account: SettingsAccountCard(
|
||||
compact: true,
|
||||
onManage: () => setState(() => selected = 0),
|
||||
),
|
||||
groupItems: true,
|
||||
items: [
|
||||
for (var index = 0; index < _categories.length; index++)
|
||||
if (_categories[index].title != 'Swipe on a file')
|
||||
NooSidebarItem(
|
||||
icon: _categories[index].icon,
|
||||
label: _categories[index].title,
|
||||
selected: selected == index,
|
||||
onTap: () => setState(() => selected = index),
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
NooToolbar(
|
||||
title: category.title,
|
||||
backgroundColor: colors.bg,
|
||||
framed: false,
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 8, 12, 16),
|
||||
child: Material(
|
||||
color: colors.surface2,
|
||||
borderRadius: BorderRadius.circular(NooRadii.card),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: AnimatedSwitcher(
|
||||
duration: duration,
|
||||
switchInCurve: Curves.easeOutCubic,
|
||||
switchOutCurve: Curves.easeInCubic,
|
||||
transitionBuilder: (child, animation) =>
|
||||
FadeTransition(
|
||||
opacity: animation,
|
||||
child: SlideTransition(
|
||||
position: Tween(
|
||||
begin: const Offset(0, .025),
|
||||
end: Offset.zero,
|
||||
).animate(animation),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
child: ListView(
|
||||
key: ValueKey(category.title),
|
||||
padding: const EdgeInsets.all(12),
|
||||
children: [
|
||||
SettingsCategoryHeading(
|
||||
title: category.title,
|
||||
child: category.builder(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: colors.bg,
|
||||
appBar: desktop
|
||||
@@ -184,12 +271,7 @@ class _MobileMenu extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// A pushed, single-category Settings screen: [NooTopBar] titled with the
|
||||
/// category, a back button, and just that section's own content - the
|
||||
/// pushed half of [AccountView]'s mobile menu-then-screen pattern. Always
|
||||
/// built in a mobile-width context (desktop never opens this screen; it
|
||||
/// shows every section inline in its own grid instead), so the section
|
||||
/// widgets inside render their normal mobile layout unchanged.
|
||||
/// A pushed category screen used by phone navigation.
|
||||
class _SettingsCategoryScreen extends StatelessWidget {
|
||||
final _SettingsCategory category;
|
||||
const _SettingsCategoryScreen({required this.category});
|
||||
@@ -223,7 +305,12 @@ class _SettingsCategoryScreen extends StatelessWidget {
|
||||
NooSpace.xxl,
|
||||
),
|
||||
physics: const BouncingScrollPhysics(),
|
||||
children: [category.builder(context)],
|
||||
children: [
|
||||
SettingsCategoryHeading(
|
||||
title: category.title,
|
||||
child: category.builder(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user