diff --git a/.github/workflows/dart_code_metrics.yaml b/.github/workflows/dart_code_metrics.yaml index 1dd9587a..03336a6a 100644 --- a/.github/workflows/dart_code_metrics.yaml +++ b/.github/workflows/dart_code_metrics.yaml @@ -8,12 +8,12 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Run Dart Code Metrics + - name: Install Flutter uses: subosito/flutter-action@v2 with: channel: stable - - name: Install dependencies + - name: Set Up DCM run: flutter pub get - uses: CQLabs/setup-dcm@v1.0.0 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 345ceb9c..10ee2492 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [2.0.0-beta.8] +✨ New ✨ +* `MacosFontWeight` allows using Apple-specific font weights like `w510`, `w590`, and `w860`. + +🛠️ Fixed 🛠️ +* `MacosTypography.black` and `MacosTypography.white` now conform to specification by using `MacosColors.labelColor` + ## [2.0.0-beta.7] ✨ New ✨ * You can now call `MacosTypography.of(context)` as a shorthand for retrieving the typography used in your `MacosTheme`. diff --git a/example/lib/main.dart b/example/lib/main.dart index 3b7d2ed2..04f48411 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -8,6 +8,7 @@ import 'package:example/pages/selectors_page.dart'; import 'package:example/pages/sliver_toolbar_page.dart'; import 'package:example/pages/tabview_page.dart'; import 'package:example/pages/toolbar_page.dart'; +import 'package:example/pages/typography_page.dart'; import 'package:flutter/cupertino.dart'; import 'package:macos_ui/macos_ui.dart'; import 'package:provider/provider.dart'; @@ -71,6 +72,7 @@ class _WidgetGalleryState extends State { (_) => const TabViewPage(), (_) => const ResizablePanePage(), (_) => const SelectorsPage(), + (_) => const TypographyPage(), ]; @override @@ -255,6 +257,10 @@ class _WidgetGalleryState extends State { ), label: Text('Selectors'), ), + SidebarItem( + leading: MacosIcon(CupertinoIcons.textformat_size), + label: Text('Typography'), + ), ], ); }, diff --git a/example/lib/pages/typography_page.dart b/example/lib/pages/typography_page.dart new file mode 100644 index 00000000..3779cb57 --- /dev/null +++ b/example/lib/pages/typography_page.dart @@ -0,0 +1,394 @@ +import 'package:flutter/cupertino.dart'; +import 'package:macos_ui/macos_ui.dart'; + +class TypographyPage extends StatelessWidget { + const TypographyPage({super.key}); + + @override + Widget build(BuildContext context) { + final typography = MacosTypography.of(context); + final secondaryTypography = MacosTypography( + color: MacosTheme.brightnessOf(context).isDark + ? MacosColors.secondaryLabelColor.darkColor + : MacosColors.secondaryLabelColor, + ); + final tertiaryTypography = MacosTypography( + color: MacosTheme.brightnessOf(context).isDark + ? MacosColors.tertiaryLabelColor.darkColor + : MacosColors.tertiaryLabelColor, + ); + + return MacosScaffold( + toolBar: const ToolBar( + title: Text('Typography'), + ), + children: [ + ContentArea( + builder: (context, scrollController) { + return ListView( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('Label Color'), + const SizedBox(height: 42.0), + Text('LargeTitle', style: typography.largeTitle), + const SizedBox(height: 8.0), + Text( + 'LargeTitle', + style: typography.largeTitle + .copyWith(fontWeight: FontWeight.w700), + ), + const SizedBox(height: 24.0), + Text('Title1', style: typography.title1), + const SizedBox(height: 8.0), + Text( + 'Title1', + style: typography.title1 + .copyWith(fontWeight: FontWeight.w700), + ), + const SizedBox(height: 24.0), + Text('Title2', style: typography.title2), + const SizedBox(height: 8.0), + Text( + 'Title2', + style: typography.title2 + .copyWith(fontWeight: FontWeight.w700), + ), + const SizedBox(height: 24.0), + Text('Title3', style: typography.title3), + const SizedBox(height: 8.0), + Text( + 'Title3', + style: typography.title3 + .copyWith(fontWeight: FontWeight.w600), + ), + const SizedBox(height: 24.0), + Text('Headline', style: typography.headline), + const SizedBox(height: 8.0), + Text( + 'Headline', + style: typography.headline + .copyWith(fontWeight: MacosFontWeight.w860), + ), + const SizedBox(height: 24.0), + Text('Body', style: typography.body), + const SizedBox(height: 8.0), + Text( + 'Body', + style: typography.body + .copyWith(fontWeight: MacosFontWeight.w590), + ), + const SizedBox(height: 24.0), + Text('Callout', style: typography.callout), + const SizedBox(height: 8.0), + Text( + 'Callout', + style: typography.callout + .copyWith(fontWeight: MacosFontWeight.w590), + ), + const SizedBox(height: 24.0), + Text('Subheadline', style: typography.subheadline), + const SizedBox(height: 8.0), + Text( + 'Subheadline', + style: typography.subheadline + .copyWith(fontWeight: MacosFontWeight.w590), + ), + const SizedBox(height: 24.0), + Text('Footnote', style: typography.subheadline), + const SizedBox(height: 8.0), + Text( + 'Footnote', + style: typography.subheadline + .copyWith(fontWeight: MacosFontWeight.w590), + ), + const SizedBox(height: 24.0), + Text('Caption1', style: typography.caption1), + const SizedBox(height: 8.0), + Text( + 'Caption1', + style: typography.caption1 + .copyWith(fontWeight: MacosFontWeight.w510), + ), + const SizedBox(height: 24.0), + Text('Caption2', style: typography.caption2), + const SizedBox(height: 8.0), + Text( + 'Caption2', + style: typography.caption2 + .copyWith(fontWeight: MacosFontWeight.w590), + ), + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('Secondary Label Color'), + const SizedBox(height: 42.0), + Text( + 'LargeTitle', + style: secondaryTypography.largeTitle, + ), + const SizedBox(height: 8.0), + Text( + 'LargeTitle', + style: secondaryTypography.largeTitle + .copyWith(fontWeight: FontWeight.w700), + ), + const SizedBox(height: 24.0), + Text( + 'Title1', + style: secondaryTypography.title1, + ), + const SizedBox(height: 8.0), + Text( + 'Title1', + style: secondaryTypography.title1 + .copyWith(fontWeight: FontWeight.w700), + ), + const SizedBox(height: 24.0), + Text( + 'Title2', + style: secondaryTypography.title2, + ), + const SizedBox(height: 8.0), + Text( + 'Title2', + style: secondaryTypography.title2 + .copyWith(fontWeight: FontWeight.w700), + ), + const SizedBox(height: 24.0), + Text( + 'Title3', + style: secondaryTypography.title3, + ), + const SizedBox(height: 8.0), + Text( + 'Title3', + style: secondaryTypography.title3 + .copyWith(fontWeight: FontWeight.w600), + ), + const SizedBox(height: 24.0), + Text( + 'Headline', + style: secondaryTypography.headline, + ), + const SizedBox(height: 8.0), + Text( + 'Headline', + style: secondaryTypography.headline + .copyWith(fontWeight: MacosFontWeight.w860), + ), + const SizedBox(height: 24.0), + Text( + 'Body', + style: secondaryTypography.body, + ), + const SizedBox(height: 8.0), + Text( + 'Body', + style: secondaryTypography.body + .copyWith(fontWeight: MacosFontWeight.w590), + ), + const SizedBox(height: 24.0), + Text( + 'Callout', + style: secondaryTypography.callout, + ), + const SizedBox(height: 8.0), + Text( + 'Callout', + style: secondaryTypography.callout + .copyWith(fontWeight: MacosFontWeight.w590), + ), + const SizedBox(height: 24.0), + Text( + 'Subheadline', + style: secondaryTypography.subheadline, + ), + const SizedBox(height: 8.0), + Text( + 'Subheadline', + style: secondaryTypography.subheadline + .copyWith(fontWeight: FontWeight.w600), + ), + const SizedBox(height: 24.0), + Text( + 'Footnote', + style: secondaryTypography.footnote, + ), + const SizedBox(height: 8.0), + Text( + 'Footnote', + style: secondaryTypography.footnote + .copyWith(fontWeight: MacosFontWeight.w590), + ), + const SizedBox(height: 24.0), + Text( + 'Caption1', + style: secondaryTypography.caption1, + ), + const SizedBox(height: 8.0), + Text( + 'Caption1', + style: secondaryTypography.caption1 + .copyWith(fontWeight: MacosFontWeight.w510), + ), + const SizedBox(height: 24.0), + Text( + 'Caption2', + style: secondaryTypography.caption2, + ), + const SizedBox(height: 8.0), + Text( + 'Caption2', + style: secondaryTypography.caption2 + .copyWith(fontWeight: MacosFontWeight.w590), + ), + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('Tertiary Label Color'), + const SizedBox(height: 42.0), + Text( + 'LargeTitle', + style: tertiaryTypography.largeTitle, + ), + const SizedBox(height: 8.0), + Text( + 'LargeTitle', + style: tertiaryTypography.largeTitle + .copyWith(fontWeight: FontWeight.w700), + ), + const SizedBox(height: 24.0), + Text( + 'Title1', + style: tertiaryTypography.title1, + ), + const SizedBox(height: 8.0), + Text( + 'Title1', + style: tertiaryTypography.title1 + .copyWith(fontWeight: FontWeight.w700), + ), + const SizedBox(height: 24.0), + Text( + 'Title2', + style: tertiaryTypography.title2, + ), + const SizedBox(height: 8.0), + Text( + 'Title2', + style: tertiaryTypography.title2 + .copyWith(fontWeight: FontWeight.w700), + ), + const SizedBox(height: 24.0), + Text( + 'Title3', + style: tertiaryTypography.title3, + ), + const SizedBox(height: 8.0), + Text( + 'Title3', + style: tertiaryTypography.title3 + .copyWith(fontWeight: FontWeight.w600), + ), + const SizedBox(height: 24.0), + Text( + 'Headline', + style: tertiaryTypography.headline, + ), + const SizedBox(height: 8.0), + Text( + 'Headline', + style: tertiaryTypography.headline + .copyWith(fontWeight: MacosFontWeight.w860), + ), + const SizedBox(height: 24.0), + Text( + 'Body', + style: tertiaryTypography.body, + ), + const SizedBox(height: 8.0), + Text( + 'Body', + style: tertiaryTypography.body + .copyWith(fontWeight: MacosFontWeight.w590), + ), + const SizedBox(height: 24.0), + Text( + 'Callout', + style: tertiaryTypography.callout, + ), + const SizedBox(height: 8.0), + Text( + 'Callout', + style: tertiaryTypography.callout + .copyWith(fontWeight: MacosFontWeight.w590), + ), + const SizedBox(height: 24.0), + Text( + 'Subheadline', + style: tertiaryTypography.subheadline, + ), + const SizedBox(height: 8.0), + Text( + 'Subheadline', + style: tertiaryTypography.subheadline + .copyWith(fontWeight: MacosFontWeight.w590), + ), + const SizedBox(height: 24.0), + Text( + 'Footnote', + style: tertiaryTypography.footnote, + ), + const SizedBox(height: 8.0), + Text( + 'Footnote', + style: tertiaryTypography.footnote + .copyWith(fontWeight: MacosFontWeight.w590), + ), + const SizedBox(height: 24.0), + Text( + 'Caption1', + style: tertiaryTypography.caption1, + ), + const SizedBox(height: 8.0), + Text( + 'Caption1', + style: tertiaryTypography.caption1 + .copyWith(fontWeight: MacosFontWeight.w510), + ), + const SizedBox(height: 24.0), + Text( + 'Caption2', + style: tertiaryTypography.caption2, + ), + const SizedBox(height: 8.0), + Text( + 'Caption2', + style: tertiaryTypography.caption2 + .copyWith(fontWeight: MacosFontWeight.w590), + ), + ], + ), + ], + ), + ), + ], + ); + }, + ), + ], + ); + } +} diff --git a/example/pubspec.lock b/example/pubspec.lock index 3f05abc6..6ef37937 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -145,7 +145,7 @@ packages: path: ".." relative: true source: path - version: "2.0.0-beta.7" + version: "2.0.0-beta.8" macos_window_utils: dependency: transitive description: diff --git a/lib/src/selectors/date_picker.dart b/lib/src/selectors/date_picker.dart index a3e20fc0..2b3cd9b0 100644 --- a/lib/src/selectors/date_picker.dart +++ b/lib/src/selectors/date_picker.dart @@ -107,9 +107,9 @@ class MacosDatePicker extends StatefulWidget { /// Allows for changing the order of day headers in the graphical Date Picker /// to Mo, Tu, We, Th, Fr, Sa, Su. - /// + /// /// This is useful for internationalization purposes, as many countries begin their weeks on Mondays. - /// + /// /// Defaults to `false`. final bool? startWeekOnMonday; diff --git a/lib/src/theme/macos_theme.dart b/lib/src/theme/macos_theme.dart index 6de99369..0cae2b9a 100644 --- a/lib/src/theme/macos_theme.dart +++ b/lib/src/theme/macos_theme.dart @@ -224,9 +224,8 @@ class MacosThemeData with Diagnosticable { ); pushButtonTheme ??= PushButtonThemeData( color: primaryColor, - secondaryColor: isDark - ? const Color.fromRGBO(110, 109, 112, 1.0) - : MacosColors.white, + secondaryColor: + isDark ? const Color.fromRGBO(110, 109, 112, 1.0) : MacosColors.white, disabledColor: isDark ? const Color.fromRGBO(255, 255, 255, 0.1) : const Color.fromRGBO(244, 245, 245, 1.0), diff --git a/lib/src/theme/typography.dart b/lib/src/theme/typography.dart index c5a9eefd..e600a3e0 100644 --- a/lib/src/theme/typography.dart +++ b/lib/src/theme/typography.dart @@ -1,5 +1,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; +import 'package:macos_ui/src/theme/macos_colors.dart'; import 'package:macos_ui/src/theme/macos_theme.dart'; const _kDefaultFontFamily = '.AppleSystemUIFont'; @@ -142,9 +143,9 @@ class MacosTypography with Diagnosticable { }); static final MacosTypography black = - MacosTypography(color: CupertinoColors.black); + MacosTypography(color: MacosColors.labelColor.color); static final MacosTypography white = - MacosTypography(color: CupertinoColors.white); + MacosTypography(color: MacosColors.labelColor.darkColor); /// Style used for body text. final TextStyle body; @@ -297,3 +298,147 @@ class MacosTypography with Diagnosticable { )); } } + +/// The thickness of the glyphs used to draw the text. +/// +/// Implements [FontWeight] in order to provide the following custom weight +/// values that Apple use in some of their text styles: +/// * [w510] +/// * [w590] +/// * [w860] +/// +/// Reference: +/// * [macOS Sonoma Figma Kit](https://www.figma.com/file/IX6ph2VWrJiRoMTI1Byz0K/Apple-Design-Resources---macOS-(Community)?node-id=0%3A1745&mode=dev) +class MacosFontWeight implements FontWeight { + const MacosFontWeight._(this.index, this.value); + + /// The encoded integer value of this font weight. + @override + final int index; + + /// The thickness value of this font weight. + @override + final int value; + + /// Thin, the least thick + static const MacosFontWeight w100 = MacosFontWeight._(0, 100); + + /// Extra-light + static const MacosFontWeight w200 = MacosFontWeight._(1, 200); + + /// Light + static const MacosFontWeight w300 = MacosFontWeight._(2, 300); + + /// Normal / regular / plain + static const MacosFontWeight w400 = MacosFontWeight._(3, 400); + + /// Medium + static const MacosFontWeight w500 = MacosFontWeight._(4, 500); + + /// An Apple-specific font weight. + /// + /// When [MacosTypography.caption1] needs to be bolded, use this value. + static const MacosFontWeight w510 = MacosFontWeight._(5, 510); + + /// An Apple-specific font weight. + /// + /// When [MacosTypography.body], [MacosTypography.callout], + /// [MacosTypography.subheadline], [MacosTypography.footnote], or + /// [MacosTypography.caption2] need to be bolded, use this value. + static const MacosFontWeight w590 = MacosFontWeight._(6, 590); + + /// Semi-bold + static const MacosFontWeight w600 = MacosFontWeight._(7, 600); + + /// Bold + static const MacosFontWeight w700 = MacosFontWeight._(8, 700); + + /// Extra-bold + static const MacosFontWeight w800 = MacosFontWeight._(9, 800); + + /// An Apple-specific font weight. + /// + /// When [MacosTypography.title3] needs to be bolded, use this value. + static const MacosFontWeight w860 = MacosFontWeight._(10, 860); + + /// Black, the most thick + static const MacosFontWeight w900 = MacosFontWeight._(11, 900); + + /// The default font weight. + static const MacosFontWeight normal = w400; + + /// A commonly used font weight that is heavier than normal. + static const MacosFontWeight bold = w700; + + /// A list of all the font weights. + static const List values = [ + w100, + w200, + w300, + w400, + w500, + w510, + w590, + w600, + w700, + w800, + w860, + w900 + ]; + + /// Linearly interpolates between two font weights. + /// + /// Rather than using fractional weights, the interpolation rounds to the + /// nearest weight. + /// + /// If both `a` and `b` are null, then this method will return null. Otherwise, + /// any null values for `a` or `b` are interpreted as equivalent to [normal] + /// (also known as [w400]). + /// + /// The `t` argument represents position on the timeline, with 0.0 meaning + /// that the interpolation has not started, returning `a` (or something + /// equivalent to `a`), 1.0 meaning that the interpolation has finished, + /// returning `b` (or something equivalent to `b`), and values in between + /// meaning that the interpolation is at the relevant point on the timeline + /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and + /// 1.0, so negative values and values greater than 1.0 are valid (and can + /// easily be generated by curves such as [Curves.elasticInOut]). The result + /// is clamped to the range [w100]–[w900]. + /// + /// Values for `t` are usually obtained from an [Animation], such as + /// an [AnimationController]. + static MacosFontWeight? lerp( + MacosFontWeight? a, MacosFontWeight? b, double t) { + if (a == null && b == null) { + return null; + } + return values[_lerpInt((a ?? normal).index, (b ?? normal).index, t) + .round() + .clamp(0, 8)]; + } + + @override + String toString() { + return const { + 0: 'MacosFontWeight.w100', + 1: 'MacosFontWeight.w200', + 2: 'MacosFontWeight.w300', + 3: 'MacosFontWeight.w400', + 4: 'MacosFontWeight.w500', + 5: 'MacosFontWeight.w510', + 6: 'MacosFontWeight.w590', + 7: 'MacosFontWeight.w600', + 8: 'MacosFontWeight.w700', + 9: 'MacosFontWeight.w800', + 10: 'MacosFontWeight.w860', + 11: 'MacosFontWeight.w900', + }[index]!; + } +} + +/// Linearly interpolate between two integers. +/// +/// Same as [lerpDouble] but specialized for non-null `int` type. +double _lerpInt(int a, int b, double t) { + return a + (b - a) * t; +} diff --git a/pubspec.yaml b/pubspec.yaml index 5ce15348..4c2877e4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: macos_ui description: Flutter widgets and themes implementing the current macOS design language. -version: 2.0.0-beta.7 +version: 2.0.0-beta.8 homepage: "https://macosui.dev" repository: "https://github.com/GroovinChip/macos_ui"