From aae8eb9f85d0a1c5c1bf127dc022fe3d08d48eca Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Fri, 4 Aug 2023 19:37:48 +0200 Subject: [PATCH 01/94] automatic changes --- example/macos/Podfile.lock | 2 +- example/pubspec.lock | 46 ++++++++++++++-------------- pubspec.lock | 62 ++++++++++++++------------------------ 3 files changed, 47 insertions(+), 63 deletions(-) diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index 2fa81174..0ee0bdce 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -38,4 +38,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: ff0a9a3ce75ee73f200ca7e2f47745698c917ef9 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.1 diff --git a/example/pubspec.lock b/example/pubspec.lock index 3dbde41a..dc586a2a 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.1" crypto: dependency: transitive description: @@ -128,6 +128,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" lints: dependency: transitive description: @@ -155,18 +163,18 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.15" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.2.0" meta: dependency: transitive description: @@ -280,26 +288,26 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.9.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" string_scanner: dependency: transitive description: @@ -320,10 +328,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.5.1" typed_data: dependency: transitive description: @@ -404,14 +412,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - web: - dependency: transitive - description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 - url: "https://pub.dev" - source: hosted - version: "0.1.4-beta" win32: dependency: transitive description: @@ -429,5 +429,5 @@ packages: source: hosted version: "1.0.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.0.0 <4.0.0" flutter: ">=3.10.0" diff --git a/pubspec.lock b/pubspec.lock index 708421c4..dfa8a61c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "0816708f5fbcacca324d811297153fe3c8e047beb5c6752e12292d2974c17045" + sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a url: "https://pub.dev" source: hosted - version: "62.0.0" + version: "61.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "21862995c9932cd082f89d72ae5f5e2c110d1a0204ad06e4ebaee8307b76b834" + sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "5.13.0" args: dependency: transitive description: @@ -61,10 +61,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.1" convert: dependency: transitive description: @@ -89,14 +89,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" - dart_internal: - dependency: transitive - description: - name: dart_internal - sha256: "689dccc3d5f62affd339534cca548dce12b3a6b32f0f10861569d3025efc0567" - url: "https://pub.dev" - source: hosted - version: "0.2.9" fake_async: dependency: transitive description: @@ -207,18 +199,18 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.15" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.2.0" meta: dependency: transitive description: @@ -340,26 +332,26 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.9.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" string_scanner: dependency: transitive description: @@ -380,26 +372,26 @@ packages: dependency: transitive description: name: test - sha256: b9a384c4b9c4966dbf7215e7c033a78db1da7e5dcaf8da9232c5f24735f65652 + sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" url: "https://pub.dev" source: hosted - version: "1.24.5" + version: "1.24.1" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.5.1" test_core: dependency: transitive description: name: test_core - sha256: c6a536288535efef8526eea8adfa4e25fdd2849fa7f457ecb2a52099998ce8f7 + sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" url: "https://pub.dev" source: hosted - version: "0.5.5" + version: "0.5.1" typed_data: dependency: transitive description: @@ -432,14 +424,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" - web: - dependency: transitive - description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 - url: "https://pub.dev" - source: hosted - version: "0.1.4-beta" web_socket_channel: dependency: transitive description: @@ -465,5 +449,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.0-185.0.dev <3.3.0" + dart: ">=3.0.0 <4.0.0" flutter: ">=3.10.0" From 968cd317b2c3935d7cec2f8e1cc2242d7e400054 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sun, 6 Aug 2023 21:39:25 +0200 Subject: [PATCH 02/94] =?UTF-8?q?imitate=20macOS=E2=80=99=20push=20button?= =?UTF-8?q?=20look?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/pubspec.lock | 8 + lib/src/buttons/push_button.dart | 389 +++++++++++++++++++++++++++++-- lib/src/enums/accent_color.dart | 11 + pubspec.lock | 8 + pubspec.yaml | 1 + 5 files changed, 396 insertions(+), 21 deletions(-) create mode 100644 lib/src/enums/accent_color.dart diff --git a/example/pubspec.lock b/example/pubspec.lock index dc586a2a..eab32583 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -112,6 +112,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.1.0" + gradient_borders: + dependency: transitive + description: + name: gradient_borders + sha256: "69eeaff519d145a4c6c213ada1abae386bcc8981a4970d923e478ce7ba19e309" + url: "https://pub.dev" + source: hosted + version: "1.0.0" http: dependency: transitive description: diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index 19180016..d1fbb238 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -2,7 +2,9 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; +import 'package:gradient_borders/gradient_borders.dart'; import 'package:macos_ui/macos_ui.dart'; +import 'package:macos_ui/src/enums/accent_color.dart'; import 'package:macos_ui/src/library.dart'; const _kMiniButtonSize = Size(26.0, 11.0); @@ -300,29 +302,64 @@ class PushButtonState extends State @visibleForTesting bool buttonHeldDown = false; - @override - Widget build(BuildContext context) { - assert(debugCheckHasMacosTheme(context)); + AccentColor get _accentColor => AccentColor.blue; // TODO: make this dynamic + + BoxDecoration _getBoxDecoration() { + return _BoxDecorationBuilder.buildBoxDecoration( + accentColor: _accentColor, + isEnabled: widget.enabled, + isDarkModeEnabled: MacosTheme.of(context).brightness.isDark, + isSecondary: widget.secondary ?? false, + ); + } + + Color _getBackgroundColor() { final bool enabled = widget.enabled; final bool isSecondary = widget.secondary != null && widget.secondary!; final MacosThemeData theme = MacosTheme.of(context); - final Color backgroundColor = MacosDynamicColor.resolve( + + return MacosDynamicColor.resolve( widget.color ?? - (isSecondary - ? theme.pushButtonTheme.secondaryColor! - : theme.pushButtonTheme.color!), + _BoxDecorationBuilder.getGradientColors( + accentColor: _accentColor, + isEnabled: enabled, + isDarkModeEnabled: theme.brightness.isDark, + isSecondary: isSecondary) + .first, context, ); + } + + Color _getForegroundColor(Color backgroundColor) { + final MacosThemeData theme = MacosTheme.of(context); - final disabledColor = !isSecondary - ? backgroundColor.withOpacity(0.5) - : backgroundColor.withOpacity(0.25); + final blendedBackgroundColor = Color.lerp( + theme.canvasColor, + backgroundColor, + backgroundColor.opacity, + )!; - final Color foregroundColor = widget.enabled - ? textLuminance(backgroundColor) - : theme.brightness.isDark - ? const Color.fromRGBO(255, 255, 255, 0.25) - : const Color.fromRGBO(0, 0, 0, 0.25); + return widget.enabled + ? textLuminance(blendedBackgroundColor) + : textLuminance(blendedBackgroundColor).withOpacity(0.25); + } + + @override + Widget build(BuildContext context) { + assert(debugCheckHasMacosTheme(context)); + final bool enabled = widget.enabled; + final MacosThemeData theme = MacosTheme.of(context); + // TODO: remove this + // final Color backgroundColor = MacosDynamicColor.resolve( + // widget.color ?? + // (isSecondary + // ? theme.pushButtonTheme.secondaryColor! + // : theme.pushButtonTheme.color!), + // context, + // ); + final Color backgroundColor = _getBackgroundColor(); + + final Color foregroundColor = _getForegroundColor(backgroundColor); final baseStyle = theme.typography.body.copyWith(color: foregroundColor); @@ -342,12 +379,8 @@ class PushButtonState extends State child: FadeTransition( opacity: _opacityAnimation, child: DecoratedBox( - decoration: ShapeDecoration( - shape: RoundedRectangleBorder( - borderRadius: widget.controlSize.borderRadius, - ), - // color: !enabled ? disabledColor : backgroundColor, - color: enabled ? backgroundColor : disabledColor, + decoration: _getBoxDecoration().copyWith( + borderRadius: widget.controlSize.borderRadius, ), child: Padding( padding: widget.controlSize.padding, @@ -369,3 +402,317 @@ class PushButtonState extends State ); } } + +class _BoxDecorationBuilder { + /// Gets the colors to use for the [BoxDecoration]’s gradient based on the + /// provided [accentColor], [isEnabled], and [isDarkModeEnabled] properties. + static List getGradientColors({ + required AccentColor accentColor, + required bool isEnabled, + required bool isDarkModeEnabled, + required bool isSecondary, + }) { + final isEnabledFactor = isEnabled ? 1.0 : 0.5; + + if (isSecondary) { + return isDarkModeEnabled + ? [ + MacosColor.fromRGBO(255, 255, 255, 0.251 * isEnabledFactor), + MacosColor.fromRGBO(255, 255, 255, 0.251 * isEnabledFactor), + ] + : [ + MacosColor.fromRGBO(255, 255, 255, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(255, 255, 255, 1.0 * isEnabledFactor), + ]; + } + + if (isDarkModeEnabled) { + switch (accentColor) { + case AccentColor.blue: + return [ + MacosColor.fromRGBO(0, 114, 238, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(0, 94, 211, 1.0 * isEnabledFactor), + ]; + + case AccentColor.purple: + return [ + MacosColor.fromRGBO(135, 65, 131, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(120, 57, 116, 1.0 * isEnabledFactor), + ]; + + case AccentColor.pink: + return [ + MacosColor.fromRGBO(188, 52, 105, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(168, 46, 93, 1.0 * isEnabledFactor), + ]; + + case AccentColor.red: + return [ + MacosColor.fromRGBO(186, 53, 46, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(166, 48, 41, 1.0 * isEnabledFactor), + ]; + + case AccentColor.orange: + return [ + MacosColor.fromRGBO(212, 133, 33, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(189, 118, 30, 1.0 * isEnabledFactor), + ]; + + case AccentColor.yellow: + return [ + MacosColor.fromRGBO(229, 203, 35, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(204, 179, 21, 1.0 * isEnabledFactor), + ]; + + case AccentColor.green: + return [ + MacosColor.fromRGBO(58, 138, 46, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(52, 123, 39, 1.0 * isEnabledFactor), + ]; + + case AccentColor.graphite: + return [ + MacosColor.fromRGBO(64, 64, 64, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(57, 57, 57, 1.0 * isEnabledFactor), + ]; + + default: + throw UnimplementedError(); + } + } else { + switch (accentColor) { + case AccentColor.blue: + return [ + MacosColor.fromRGBO(39, 125, 255, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(1, 101, 255, 1.0 * isEnabledFactor), + ]; + + case AccentColor.purple: + return [ + MacosColor.fromRGBO(148, 73, 143, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(128, 39, 121, 1.0 * isEnabledFactor), + ]; + + case AccentColor.pink: + return [ + MacosColor.fromRGBO(212, 71, 125, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(203, 36, 101, 1.0 * isEnabledFactor), + ]; + + case AccentColor.red: + return [ + MacosColor.fromRGBO(198, 64, 57, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(188, 29, 21, 1.0 * isEnabledFactor), + ]; + + case AccentColor.orange: + return [ + MacosColor.fromRGBO(237, 154, 51, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(234, 136, 13, 1.0 * isEnabledFactor), + ]; + + case AccentColor.yellow: + return [ + MacosColor.fromRGBO(242, 211, 61, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(240, 203, 25, 1.0 * isEnabledFactor), + ]; + + case AccentColor.green: + return [ + MacosColor.fromRGBO(77, 161, 63, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(45, 143, 28, 1.0 * isEnabledFactor), + ]; + + case AccentColor.graphite: + return [ + MacosColor.fromRGBO(86, 86, 86, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(55, 55, 55, 1.0 * isEnabledFactor), + ]; + + default: + throw UnimplementedError(); + } + } + } + + /// Gets the shadow to use for the [BoxDecoration] based on the provided + /// [accentColor], [isEnabled], and [isDarkModeEnabled] properties. + static List _getShadow({ + required AccentColor accentColor, + required bool isEnabled, + required bool isDarkModeEnabled, + required bool isSecondary, + }) { + final isEnabledFactor = isEnabled ? 1.0 : 0.5; + + if (isSecondary) { + return isDarkModeEnabled + ? [ + BoxShadow( + color: MacosColor.fromRGBO(0, 0, 0, 0.4 * isEnabledFactor), + blurRadius: 0.5, + offset: Offset.zero, + spreadRadius: 0.0, + blurStyle: BlurStyle.outer, + ), + ] + : [ + BoxShadow( + color: MacosColor.fromRGBO(0, 0, 0, 0.4 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + } + + if (isDarkModeEnabled) { + return [ + BoxShadow( + color: MacosColor.fromRGBO(0, 0, 0, 0.4 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + } else { + switch (accentColor) { + case AccentColor.blue: + return [ + BoxShadow( + color: MacosColor.fromRGBO(0, 103, 255, 0.21 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + + case AccentColor.purple: + return [ + BoxShadow( + color: MacosColor.fromRGBO(139, 29, 125, 0.21 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + + case AccentColor.pink: + return [ + BoxShadow( + color: MacosColor.fromRGBO(222, 0, 101, 0.21 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + + case AccentColor.red: + return [ + BoxShadow( + color: MacosColor.fromRGBO(188, 29, 21, 0.35 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + + case AccentColor.orange: + return [ + BoxShadow( + color: MacosColor.fromRGBO(234, 136, 13, 0.35 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + + case AccentColor.yellow: + return [ + BoxShadow( + color: MacosColor.fromRGBO(240, 203, 25, 0.35 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + + case AccentColor.green: + return [ + BoxShadow( + color: MacosColor.fromRGBO(45, 143, 28, 0.35 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + + case AccentColor.graphite: + return [ + BoxShadow( + color: MacosColor.fromRGBO(55, 55, 55, 0.35 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + + default: + throw UnimplementedError(); + } + } + } + + /// Builds a [BoxDecoration] for a [MacosPushButton]. + static BoxDecoration buildBoxDecoration({ + required AccentColor accentColor, + required bool isEnabled, + required bool isDarkModeEnabled, + required bool isSecondary, + }) { + final isEnabledFactor = isEnabled ? 1.0 : 0.5; + + return BoxDecoration( + border: isDarkModeEnabled + ? GradientBoxBorder( + gradient: LinearGradient( + colors: [ + MacosColor.fromRGBO(255, 255, 255, 0.43 * isEnabledFactor), + const MacosColor.fromRGBO(255, 255, 255, 0.0), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + stops: const [0.0, 0.2], + ), + width: 0.7, + ) + : null, + gradient: LinearGradient( + colors: getGradientColors( + accentColor: accentColor, + isEnabled: isEnabled, + isDarkModeEnabled: isDarkModeEnabled, + isSecondary: isSecondary, + ), + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + boxShadow: _getShadow( + accentColor: accentColor, + isEnabled: isEnabled, + isDarkModeEnabled: isDarkModeEnabled, + isSecondary: isSecondary, + ), + ); + } +} diff --git a/lib/src/enums/accent_color.dart b/lib/src/enums/accent_color.dart new file mode 100644 index 00000000..ed21cfe8 --- /dev/null +++ b/lib/src/enums/accent_color.dart @@ -0,0 +1,11 @@ +// TODO: document this +enum AccentColor { + blue, + purple, + pink, + red, + orange, + yellow, + green, + graphite, +} diff --git a/pubspec.lock b/pubspec.lock index dfa8a61c..be900083 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -139,6 +139,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + gradient_borders: + dependency: "direct main" + description: + name: gradient_borders + sha256: "69eeaff519d145a4c6c213ada1abae386bcc8981a4970d923e478ce7ba19e309" + url: "https://pub.dev" + source: hosted + version: "1.0.0" http_multi_server: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 13865b31..7b208ea5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,6 +12,7 @@ dependencies: flutter: sdk: flutter macos_window_utils: ^1.1.3 + gradient_borders: ^1.0.0 dev_dependencies: flutter_test: From b93084c1bff0e4f9f5d630d5c67fa5e93a3564ff Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sun, 6 Aug 2023 22:51:54 +0200 Subject: [PATCH 03/94] make push button appear secondary when window resigns main state --- lib/src/buttons/push_button.dart | 15 ++++++- lib/src/utils.dart | 72 ++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index d1fbb238..3aa9cfde 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -1,5 +1,7 @@ // ignore_for_file: prefer_if_null_operators +import 'dart:async'; + import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:gradient_borders/gradient_borders.dart'; @@ -237,6 +239,8 @@ class PushButtonState extends State late AnimationController _animationController; late Animation _opacityAnimation; + late StreamSubscription _onWindowMainStateChangedStreamSubcription; + @override void initState() { super.initState(); @@ -249,6 +253,10 @@ class PushButtonState extends State .drive(CurveTween(curve: Curves.decelerate)) .drive(_opacityTween); _setTween(); + + _onWindowMainStateChangedStreamSubcription = WindowMainStateListener + .instance.onChangedStream + .listen((_) => setState(() {})); } @override @@ -296,6 +304,7 @@ class PushButtonState extends State @override void dispose() { _animationController.dispose(); + _onWindowMainStateChangedStreamSubcription.cancel(); super.dispose(); } @@ -305,11 +314,15 @@ class PushButtonState extends State AccentColor get _accentColor => AccentColor.blue; // TODO: make this dynamic BoxDecoration _getBoxDecoration() { + // If the window isn’t currently the main window (that is, it is not in + // focus), make the button look as if it was a secondary button. + final isWindowMain = WindowMainStateListener.instance.isWindowMain; + return _BoxDecorationBuilder.buildBoxDecoration( accentColor: _accentColor, isEnabled: widget.enabled, isDarkModeEnabled: MacosTheme.of(context).brightness.isDark, - isSecondary: widget.secondary ?? false, + isSecondary: !isWindowMain || (widget.secondary ?? false), ); } diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 1f2d8a1e..dcb8212a 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; @@ -86,3 +87,74 @@ class MacOSBrightnessOverrideHandler { _lastBrightness = currentBrightness; } } + +/// A class that listens for changes to the application's window being the main +/// window, and notifies listeners. +class WindowMainStateListener { + /// A shared instance of [WindowMainStateListener]. + static final instance = WindowMainStateListener(); + + /// A [NSWindowDelegateHandle], to be used when deiniting the listener. + NSWindowDelegateHandle? handle; + + /// Whether the window is currently the main window. + bool _isWindowMain = + true; // TODO: Initialize properly once macos_window_utils supports that, + // see https://github.com/macosui/macos_window_utils.dart/issues/31. + + /// Whether the window is currently the main window. + bool get isWindowMain => _isWindowMain; + + /// Notifies listeners when the window’s main state changes. + final _windowMainStateStreamController = StreamController.broadcast(); + + /// A stream of the window’s main state. Emits a new value whenever the state + /// changes. + Stream get onChangedStream => _windowMainStateStreamController.stream; + + /// Initializes the listener. This should only be called once. + void _init() { + final delegate = _WindowMainStateListenerDelegate( + onWindowDidBecomeMain: () { + _isWindowMain = true; + _windowMainStateStreamController.add(true); + }, + onWindowDidResignMain: () { + _isWindowMain = false; + _windowMainStateStreamController.add(false); + }, + ); + handle = WindowManipulator.addNSWindowDelegate(delegate); + } + + /// Deinitializes the listener. + void deinit() { + handle?.removeFromHandler(); + } + + /// A class that listens for changes to the application's window being the + /// main window, and notifies listeners. + WindowMainStateListener() { + _init(); + } +} + +/// The [NSWindowDelegate] used by [WindowMainStateListener]. +class _WindowMainStateListenerDelegate extends NSWindowDelegate { + _WindowMainStateListenerDelegate({ + required this.onWindowDidBecomeMain, + required this.onWindowDidResignMain, + }); + + /// Called when the window becomes the main window. + final void Function() onWindowDidBecomeMain; + + /// Called when the window resigns as the main window. + final void Function() onWindowDidResignMain; + + @override + void windowDidBecomeMain() => onWindowDidBecomeMain(); + + @override + void windowDidResignMain() => onWindowDidResignMain(); +} From b4d22f1ff340547c365911ae42f42bfd3768d85b Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sun, 6 Aug 2023 22:58:36 +0200 Subject: [PATCH 04/94] migrate to stream builder --- lib/src/buttons/push_button.dart | 45 +++++++++++++++----------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index 3aa9cfde..ad7be046 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -239,8 +239,6 @@ class PushButtonState extends State late AnimationController _animationController; late Animation _opacityAnimation; - late StreamSubscription _onWindowMainStateChangedStreamSubcription; - @override void initState() { super.initState(); @@ -253,10 +251,6 @@ class PushButtonState extends State .drive(CurveTween(curve: Curves.decelerate)) .drive(_opacityTween); _setTween(); - - _onWindowMainStateChangedStreamSubcription = WindowMainStateListener - .instance.onChangedStream - .listen((_) => setState(() {})); } @override @@ -304,7 +298,6 @@ class PushButtonState extends State @override void dispose() { _animationController.dispose(); - _onWindowMainStateChangedStreamSubcription.cancel(); super.dispose(); } @@ -391,23 +384,27 @@ class PushButtonState extends State constraints: widget.controlSize.constraints, child: FadeTransition( opacity: _opacityAnimation, - child: DecoratedBox( - decoration: _getBoxDecoration().copyWith( - borderRadius: widget.controlSize.borderRadius, - ), - child: Padding( - padding: widget.controlSize.padding, - child: Align( - alignment: widget.alignment, - widthFactor: 1.0, - heightFactor: 1.0, - child: DefaultTextStyle( - style: widget.controlSize.textStyle(baseStyle), - child: widget.child, - ), - ), - ), - ), + child: StreamBuilder( + stream: WindowMainStateListener.instance.onChangedStream, + builder: (context, _) { + return DecoratedBox( + decoration: _getBoxDecoration().copyWith( + borderRadius: widget.controlSize.borderRadius, + ), + child: Padding( + padding: widget.controlSize.padding, + child: Align( + alignment: widget.alignment, + widthFactor: 1.0, + heightFactor: 1.0, + child: DefaultTextStyle( + style: widget.controlSize.textStyle(baseStyle), + child: widget.child, + ), + ), + ), + ); + }), ), ), ), From afc3a9c9006d68252116dd3944612c1fa8120a78 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sun, 6 Aug 2023 23:05:38 +0200 Subject: [PATCH 05/94] fix incorrect text color in push button when window resigns main state --- lib/src/buttons/push_button.dart | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index ad7be046..1979204b 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -324,14 +324,18 @@ class PushButtonState extends State final bool isSecondary = widget.secondary != null && widget.secondary!; final MacosThemeData theme = MacosTheme.of(context); + // If the window isn’t currently the main window (that is, it is not in + // focus), make the button look as if it was a secondary button. + final isWindowMain = WindowMainStateListener.instance.isWindowMain; + return MacosDynamicColor.resolve( widget.color ?? _BoxDecorationBuilder.getGradientColors( - accentColor: _accentColor, - isEnabled: enabled, - isDarkModeEnabled: theme.brightness.isDark, - isSecondary: isSecondary) - .first, + accentColor: _accentColor, + isEnabled: enabled, + isDarkModeEnabled: theme.brightness.isDark, + isSecondary: isSecondary || !isWindowMain, + ).first, context, ); } @@ -363,11 +367,6 @@ class PushButtonState extends State // : theme.pushButtonTheme.color!), // context, // ); - final Color backgroundColor = _getBackgroundColor(); - - final Color foregroundColor = _getForegroundColor(backgroundColor); - - final baseStyle = theme.typography.body.copyWith(color: foregroundColor); return MouseRegion( cursor: widget.mouseCursor!, @@ -387,6 +386,14 @@ class PushButtonState extends State child: StreamBuilder( stream: WindowMainStateListener.instance.onChangedStream, builder: (context, _) { + final Color backgroundColor = _getBackgroundColor(); + + final Color foregroundColor = + _getForegroundColor(backgroundColor); + + final baseStyle = + theme.typography.body.copyWith(color: foregroundColor); + return DecoratedBox( decoration: _getBoxDecoration().copyWith( borderRadius: widget.controlSize.borderRadius, From ab3092e9022a47c3b2a93b25df8b6b3da333355b Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 12:27:17 +0200 Subject: [PATCH 06/94] add missing comma --- lib/src/buttons/push_button.dart | 53 ++++++++++++++++---------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index 1979204b..2f714bb6 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -384,34 +384,35 @@ class PushButtonState extends State child: FadeTransition( opacity: _opacityAnimation, child: StreamBuilder( - stream: WindowMainStateListener.instance.onChangedStream, - builder: (context, _) { - final Color backgroundColor = _getBackgroundColor(); - - final Color foregroundColor = - _getForegroundColor(backgroundColor); - - final baseStyle = - theme.typography.body.copyWith(color: foregroundColor); - - return DecoratedBox( - decoration: _getBoxDecoration().copyWith( - borderRadius: widget.controlSize.borderRadius, - ), - child: Padding( - padding: widget.controlSize.padding, - child: Align( - alignment: widget.alignment, - widthFactor: 1.0, - heightFactor: 1.0, - child: DefaultTextStyle( - style: widget.controlSize.textStyle(baseStyle), - child: widget.child, - ), + stream: WindowMainStateListener.instance.onChangedStream, + builder: (context, _) { + final Color backgroundColor = _getBackgroundColor(); + + final Color foregroundColor = + _getForegroundColor(backgroundColor); + + final baseStyle = + theme.typography.body.copyWith(color: foregroundColor); + + return DecoratedBox( + decoration: _getBoxDecoration().copyWith( + borderRadius: widget.controlSize.borderRadius, + ), + child: Padding( + padding: widget.controlSize.padding, + child: Align( + alignment: widget.alignment, + widthFactor: 1.0, + heightFactor: 1.0, + child: DefaultTextStyle( + style: widget.controlSize.textStyle(baseStyle), + child: widget.child, ), ), - ); - }), + ), + ); + }, + ), ), ), ), From cb701132dce2169a1302c3d51f95f88004eaf3f6 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:02:36 +0200 Subject: [PATCH 07/94] implement `AccentColorListener` --- .../Flutter/GeneratedPluginRegistrant.swift | 2 + example/macos/Podfile.lock | 6 + example/pubspec.lock | 16 +++ lib/src/utils.dart | 133 ++++++++++++++++++ pubspec.lock | 24 ++++ pubspec.yaml | 1 + 6 files changed, 182 insertions(+) diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift index 0179c12c..1e64f18d 100644 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,12 +5,14 @@ import FlutterMacOS import Foundation +import appkit_ui_element_colors import macos_ui import macos_window_utils import path_provider_foundation import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + AppkitUiElementColorsPlugin.register(with: registry.registrar(forPlugin: "AppkitUiElementColorsPlugin")) MacOSUiPlugin.register(with: registry.registrar(forPlugin: "MacOSUiPlugin")) MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index 0ee0bdce..6aa1d86e 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -1,4 +1,6 @@ PODS: + - appkit_ui_element_colors (1.0.0): + - FlutterMacOS - FlutterMacOS (1.0.0) - macos_ui (0.1.0): - FlutterMacOS @@ -11,6 +13,7 @@ PODS: - FlutterMacOS DEPENDENCIES: + - appkit_ui_element_colors (from `Flutter/ephemeral/.symlinks/plugins/appkit_ui_element_colors/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - macos_ui (from `Flutter/ephemeral/.symlinks/plugins/macos_ui/macos`) - macos_window_utils (from `Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos`) @@ -18,6 +21,8 @@ DEPENDENCIES: - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) EXTERNAL SOURCES: + appkit_ui_element_colors: + :path: Flutter/ephemeral/.symlinks/plugins/appkit_ui_element_colors/macos FlutterMacOS: :path: Flutter/ephemeral macos_ui: @@ -30,6 +35,7 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos SPEC CHECKSUMS: + appkit_ui_element_colors: 39bb2d80be3f19b152ccf4c70d5bbe6cba43d74a FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 macos_ui: 6229a8922cd97bafb7d9636c8eb8dfb0744183ca macos_window_utils: 933f91f64805e2eb91a5bd057cf97cd097276663 diff --git a/example/pubspec.lock b/example/pubspec.lock index eab32583..f642de3a 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + appkit_ui_element_colors: + dependency: transitive + description: + name: appkit_ui_element_colors + sha256: c3e50f900aae314d339de489535736238627071457c4a4a2dbbb1545b4f04f22 + url: "https://pub.dev" + source: hosted + version: "1.0.0" async: dependency: transitive description: @@ -57,6 +65,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" + equatable: + dependency: transitive + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" fake_async: dependency: transitive description: diff --git a/lib/src/utils.dart b/lib/src/utils.dart index dcb8212a..d71b8cd4 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -1,8 +1,11 @@ import 'dart:async'; import 'dart:io'; +import 'dart:math'; +import 'package:appkit_ui_element_colors/appkit_ui_element_colors.dart'; import 'package:flutter/foundation.dart'; import 'package:macos_ui/macos_ui.dart'; +import 'package:macos_ui/src/enums/accent_color.dart'; import 'package:macos_ui/src/library.dart'; /// Asserts that the given context has a [MacosTheme] ancestor. @@ -158,3 +161,133 @@ class _WindowMainStateListenerDelegate extends NSWindowDelegate { @override void windowDidResignMain() => onWindowDidResignMain(); } + +/// A class that listens to accent color changes. +class AccentColorListener { + /// A shared instance of [AccentColorListener]. + static final instance = AccentColorListener(); + + /// A map which maps hue components of the [UiElementColor.controlAccentColor] + /// color captured with the [NSAppearanceName.aqua] appearance in the + /// [NSColorSpace.genericRGB] color space to the corresponding [AccentColor]. + static final hueComponentToAccentColor = { + 0.6085324903200698: AccentColor.blue, + 0.8285987697113538: AccentColor.purple, + 0.9209523937489168: AccentColor.pink, + 0.9861913496946438: AccentColor.red, + 0.06543037411201169: AccentColor.orange, + 0.11813830353929083: AccentColor.yellow, + 0.29428158007138466: AccentColor.green, + 0.0: AccentColor.graphite, + }; + + /// The currently active accent color. + AccentColor? _currentAccentColor; + + /// The currently active accent color. + AccentColor? get currentAccentColor => _currentAccentColor; + + /// Notifies listeners when the accent color changes. + final _accentColorStreamController = StreamController.broadcast(); + + /// An accent color stream. Emits a new value whenever the accent color + /// changes. + Stream get onChangedStream => _accentColorStreamController.stream; + + /// A stream subcription for the [SystemColorObserver] stream. + StreamSubscription? _systemColorObserverStreamSubscription; + + /// Initializes this class. + void _init() { + _initCurrentAccentColor(); + _initSystemColorObserver(); + } + + /// Deinitializes this class. + void deinit() { + _systemColorObserverStreamSubscription?.cancel(); + } + + /// Initializes the current accent color. This method is to be called whenever + /// a change is detected. + Future _initCurrentAccentColor() async { + final hueComponent = await _getHueComponent(); + _currentAccentColor = _resolveAccentColorFromHueComponent(hueComponent); + _accentColorStreamController.add(null); + } + + /// Initializes the current system color observer. This method may only be + /// called once. + void _initSystemColorObserver() { + assert(_systemColorObserverStreamSubscription == null); + + _systemColorObserverStreamSubscription = + AppkitUiElementColors.systemColorObserver.stream.listen((_) { + _initCurrentAccentColor(); + _accentColorStreamController.add(null); + }); + } + + /// Returns the hue component of the currently active accent color on macOS. + Future _getHueComponent() async { + final color = await AppkitUiElementColors.getColorComponents( + uiElementColor: UiElementColor.controlAccentColor, + components: const { + NSColorComponent.hueComponent, + }, + colorSpace: NSColorSpace.genericRGB, + appearance: NSAppearanceName.aqua, + ); + + assert(color.containsKey("hueComponent")); + + return color["hueComponent"]!; + } + + /// Returns the [AccentColor] which corresponds to the provided + /// [hueComponent]. + AccentColor _resolveAccentColorFromHueComponent(double hueComponent) { + if (hueComponentToAccentColor.containsKey(hueComponent)) { + return hueComponentToAccentColor[hueComponent]!; + } + + // ignore: avoid_print + print( + 'Warning: Falling back on slow accent color resolution. It’s possible ' + 'that the accent colors have changed in a recent version of macOS, thus ' + 'invalidating macos_ui’s accent colors, which were captured in macOS ' + 'Ventura. If you see this message, please notify a maintainer of the ' + 'macos_ui package.', + ); + + return _slowlyResolveAccentColorFromHueComponent(hueComponent); + } + + /// This is a fallback method in case the above method fails. + AccentColor _slowlyResolveAccentColorFromHueComponent(double hueComponent) { + final entries = hueComponentToAccentColor.entries; + var lowestDistance = double.maxFinite; + var toBeReturnedAccentColor = AccentColor.values.first; + + for (var entry in entries) { + final distance = _distanceBetweenHueComponents(hueComponent, entry.key); + if (distance < lowestDistance) { + lowestDistance = distance; + toBeReturnedAccentColor = entry.value; + } + } + + return toBeReturnedAccentColor; + } + + /// Returns the distance between two hue components. + double _distanceBetweenHueComponents(double component1, double component2) { + final rawDifference = (component1 - component2).abs(); + return sin(rawDifference * pi); + } + + /// A class that listens to accent color changes. + AccentColorListener() { + _init(); + } +} diff --git a/pubspec.lock b/pubspec.lock index be900083..018f3b58 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.13.0" + appkit_ui_element_colors: + dependency: "direct main" + description: + name: appkit_ui_element_colors + sha256: c3e50f900aae314d339de489535736238627071457c4a4a2dbbb1545b4f04f22 + url: "https://pub.dev" + source: hosted + version: "1.0.0" args: dependency: transitive description: @@ -89,6 +97,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" + equatable: + dependency: transitive + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" fake_async: dependency: transitive description: @@ -267,6 +283,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.3" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" pool: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 7b208ea5..64a10b1e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,6 +13,7 @@ dependencies: sdk: flutter macos_window_utils: ^1.1.3 gradient_borders: ^1.0.0 + appkit_ui_element_colors: ^1.0.0 dev_dependencies: flutter_test: From 6e77ab91c1f788dd90538ea82127567266ccc199 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:02:47 +0200 Subject: [PATCH 08/94] make push button listen to accent color changes --- lib/src/buttons/push_button.dart | 68 +++++++++++++++++--------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index 2f714bb6..27b7d4d5 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -2,6 +2,7 @@ import 'dart:async'; +import 'package:appkit_ui_element_colors/appkit_ui_element_colors.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:gradient_borders/gradient_borders.dart'; @@ -304,7 +305,8 @@ class PushButtonState extends State @visibleForTesting bool buttonHeldDown = false; - AccentColor get _accentColor => AccentColor.blue; // TODO: make this dynamic + AccentColor get _accentColor => + AccentColorListener.instance.currentAccentColor ?? AccentColor.blue; BoxDecoration _getBoxDecoration() { // If the window isn’t currently the main window (that is, it is not in @@ -383,36 +385,40 @@ class PushButtonState extends State constraints: widget.controlSize.constraints, child: FadeTransition( opacity: _opacityAnimation, - child: StreamBuilder( - stream: WindowMainStateListener.instance.onChangedStream, - builder: (context, _) { - final Color backgroundColor = _getBackgroundColor(); - - final Color foregroundColor = - _getForegroundColor(backgroundColor); - - final baseStyle = - theme.typography.body.copyWith(color: foregroundColor); - - return DecoratedBox( - decoration: _getBoxDecoration().copyWith( - borderRadius: widget.controlSize.borderRadius, - ), - child: Padding( - padding: widget.controlSize.padding, - child: Align( - alignment: widget.alignment, - widthFactor: 1.0, - heightFactor: 1.0, - child: DefaultTextStyle( - style: widget.controlSize.textStyle(baseStyle), - child: widget.child, - ), - ), - ), - ); - }, - ), + child: StreamBuilder( + stream: AccentColorListener.instance.onChangedStream, + builder: (context, _) { + return StreamBuilder( + stream: WindowMainStateListener.instance.onChangedStream, + builder: (context, _) { + final Color backgroundColor = _getBackgroundColor(); + + final Color foregroundColor = + _getForegroundColor(backgroundColor); + + final baseStyle = theme.typography.body + .copyWith(color: foregroundColor); + + return DecoratedBox( + decoration: _getBoxDecoration().copyWith( + borderRadius: widget.controlSize.borderRadius, + ), + child: Padding( + padding: widget.controlSize.padding, + child: Align( + alignment: widget.alignment, + widthFactor: 1.0, + heightFactor: 1.0, + child: DefaultTextStyle( + style: widget.controlSize.textStyle(baseStyle), + child: widget.child, + ), + ), + ), + ); + }, + ); + }), ), ), ), From ba42d7528b71359016f7ee8f4b924b9934be7978 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:03:03 +0200 Subject: [PATCH 09/94] add missing comma --- lib/src/buttons/push_button.dart | 63 ++++++++++++++++---------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index 27b7d4d5..6bea8db5 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -386,39 +386,40 @@ class PushButtonState extends State child: FadeTransition( opacity: _opacityAnimation, child: StreamBuilder( - stream: AccentColorListener.instance.onChangedStream, - builder: (context, _) { - return StreamBuilder( - stream: WindowMainStateListener.instance.onChangedStream, - builder: (context, _) { - final Color backgroundColor = _getBackgroundColor(); - - final Color foregroundColor = - _getForegroundColor(backgroundColor); - - final baseStyle = theme.typography.body - .copyWith(color: foregroundColor); - - return DecoratedBox( - decoration: _getBoxDecoration().copyWith( - borderRadius: widget.controlSize.borderRadius, - ), - child: Padding( - padding: widget.controlSize.padding, - child: Align( - alignment: widget.alignment, - widthFactor: 1.0, - heightFactor: 1.0, - child: DefaultTextStyle( - style: widget.controlSize.textStyle(baseStyle), - child: widget.child, - ), + stream: AccentColorListener.instance.onChangedStream, + builder: (context, _) { + return StreamBuilder( + stream: WindowMainStateListener.instance.onChangedStream, + builder: (context, _) { + final Color backgroundColor = _getBackgroundColor(); + + final Color foregroundColor = + _getForegroundColor(backgroundColor); + + final baseStyle = theme.typography.body + .copyWith(color: foregroundColor); + + return DecoratedBox( + decoration: _getBoxDecoration().copyWith( + borderRadius: widget.controlSize.borderRadius, + ), + child: Padding( + padding: widget.controlSize.padding, + child: Align( + alignment: widget.alignment, + widthFactor: 1.0, + heightFactor: 1.0, + child: DefaultTextStyle( + style: widget.controlSize.textStyle(baseStyle), + child: widget.child, ), ), - ); - }, - ); - }), + ), + ); + }, + ); + }, + ), ), ), ), From a549bca66b70568795f8bcf892e50f0aa31f3c2e Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:09:07 +0200 Subject: [PATCH 10/94] deprecate `PushButtonTheme` --- lib/src/buttons/push_button.dart | 8 -------- lib/src/theme/push_button_theme.dart | 4 ++++ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index 6bea8db5..e37d8828 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -361,14 +361,6 @@ class PushButtonState extends State assert(debugCheckHasMacosTheme(context)); final bool enabled = widget.enabled; final MacosThemeData theme = MacosTheme.of(context); - // TODO: remove this - // final Color backgroundColor = MacosDynamicColor.resolve( - // widget.color ?? - // (isSecondary - // ? theme.pushButtonTheme.secondaryColor! - // : theme.pushButtonTheme.color!), - // context, - // ); return MouseRegion( cursor: widget.mouseCursor!, diff --git a/lib/src/theme/push_button_theme.dart b/lib/src/theme/push_button_theme.dart index c7d27039..dc35c2b7 100644 --- a/lib/src/theme/push_button_theme.dart +++ b/lib/src/theme/push_button_theme.dart @@ -7,6 +7,8 @@ import 'package:macos_ui/src/library.dart'; /// See also: /// /// * [PushButtonThemeData], which is used to configure this theme. +@Deprecated("'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") class PushButtonTheme extends InheritedTheme { /// Create a [PushButtonTheme]. /// @@ -54,6 +56,8 @@ class PushButtonTheme extends InheritedTheme { /// * [PushButtonTheme], the theme which is configured with this class. /// * [MacosThemeData.pushButtonTheme], which can be used to override the default /// style for [PushButton]s below the overall [MacosTheme]. +@Deprecated("'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") class PushButtonThemeData with Diagnosticable { /// Creates a [PushButtonThemeData]. const PushButtonThemeData({ From ba3d7ce39a45dc79051d3b41dbdc985ab3c9fd42 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:39:58 +0200 Subject: [PATCH 11/94] =?UTF-8?q?mimic=20macOS=E2=80=99=20push=20button=20?= =?UTF-8?q?click=20effect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/src/buttons/push_button.dart | 119 ++++++++++++------------------- 1 file changed, 44 insertions(+), 75 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index e37d8828..f25f312f 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -232,76 +232,29 @@ class PushButton extends StatefulWidget { class PushButtonState extends State with SingleTickerProviderStateMixin { - // Eyeballed values. Feel free to tweak. - static const Duration kFadeOutDuration = Duration(milliseconds: 10); - static const Duration kFadeInDuration = Duration(milliseconds: 100); - final Tween _opacityTween = Tween(begin: 1.0); - - late AnimationController _animationController; - late Animation _opacityAnimation; - - @override - void initState() { - super.initState(); - _animationController = AnimationController( - duration: const Duration(milliseconds: 200), - value: 0.0, - vsync: this, - ); - _opacityAnimation = _animationController - .drive(CurveTween(curve: Curves.decelerate)) - .drive(_opacityTween); - _setTween(); - } - @override void didUpdateWidget(PushButton oldWidget) { super.didUpdateWidget(oldWidget); - _setTween(); - } - - void _setTween() { - _opacityTween.end = widget.pressedOpacity ?? 1.0; } void _handleTapDown(TapDownDetails event) { if (!buttonHeldDown) { - buttonHeldDown = true; - _animate(); + setState(() => buttonHeldDown = true); } } void _handleTapUp(TapUpDetails event) { if (buttonHeldDown) { - buttonHeldDown = false; - _animate(); + setState(() => buttonHeldDown = false); } } void _handleTapCancel() { if (buttonHeldDown) { - buttonHeldDown = false; - _animate(); + setState(() => buttonHeldDown = false); } } - void _animate() { - if (_animationController.isAnimating) return; - final bool wasHeldDown = buttonHeldDown; - final TickerFuture ticker = buttonHeldDown - ? _animationController.animateTo(1.0, duration: kFadeOutDuration) - : _animationController.animateTo(0.0, duration: kFadeInDuration); - ticker.then((void value) { - if (mounted && wasHeldDown != buttonHeldDown) _animate(); - }); - } - - @override - void dispose() { - _animationController.dispose(); - super.dispose(); - } - @visibleForTesting bool buttonHeldDown = false; @@ -356,6 +309,20 @@ class PushButtonState extends State : textLuminance(blendedBackgroundColor).withOpacity(0.25); } + BoxDecoration _getClickEffectBoxDecoration() { + final MacosThemeData theme = MacosTheme.of(context); + final isDark = theme.brightness.isDark; + + final color = isDark + ? const MacosColor.fromRGBO(255, 255, 255, 0.15) + : const MacosColor.fromRGBO(0, 0, 0, 0.06); + + return BoxDecoration( + color: color, + borderRadius: widget.controlSize.borderRadius, + ); + } + @override Widget build(BuildContext context) { assert(debugCheckHasMacosTheme(context)); @@ -375,26 +342,28 @@ class PushButtonState extends State label: widget.semanticLabel, child: ConstrainedBox( constraints: widget.controlSize.constraints, - child: FadeTransition( - opacity: _opacityAnimation, - child: StreamBuilder( - stream: AccentColorListener.instance.onChangedStream, - builder: (context, _) { - return StreamBuilder( - stream: WindowMainStateListener.instance.onChangedStream, - builder: (context, _) { - final Color backgroundColor = _getBackgroundColor(); - - final Color foregroundColor = - _getForegroundColor(backgroundColor); - - final baseStyle = theme.typography.body - .copyWith(color: foregroundColor); - - return DecoratedBox( - decoration: _getBoxDecoration().copyWith( - borderRadius: widget.controlSize.borderRadius, - ), + child: StreamBuilder( + stream: AccentColorListener.instance.onChangedStream, + builder: (context, _) { + return StreamBuilder( + stream: WindowMainStateListener.instance.onChangedStream, + builder: (context, _) { + final Color backgroundColor = _getBackgroundColor(); + + final Color foregroundColor = + _getForegroundColor(backgroundColor); + + final baseStyle = + theme.typography.body.copyWith(color: foregroundColor); + + return DecoratedBox( + decoration: _getBoxDecoration().copyWith( + borderRadius: widget.controlSize.borderRadius, + ), + child: Container( + foregroundDecoration: buttonHeldDown + ? _getClickEffectBoxDecoration() + : const BoxDecoration(), child: Padding( padding: widget.controlSize.padding, child: Align( @@ -407,11 +376,11 @@ class PushButtonState extends State ), ), ), - ); - }, - ); - }, - ), + ), + ); + }, + ); + }, ), ), ), From 07490f0edb586e4eabca04c9418309467dc21916 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:42:05 +0200 Subject: [PATCH 12/94] =?UTF-8?q?do=20not=20init=20`WindowMainStateListene?= =?UTF-8?q?r`=20if=20platform=20isn=E2=80=99t=20macOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/src/utils.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index d71b8cd4..cf18f817 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -117,6 +117,9 @@ class WindowMainStateListener { /// Initializes the listener. This should only be called once. void _init() { + if (kIsWeb) return; + if (!Platform.isMacOS) return; + final delegate = _WindowMainStateListenerDelegate( onWindowDidBecomeMain: () { _isWindowMain = true; From aa8f90d1a708ea0710e2a27ac42b2bacffede59a Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:42:27 +0200 Subject: [PATCH 13/94] =?UTF-8?q?do=20not=20init=20`AccentColorListener`?= =?UTF-8?q?=20if=20platform=20isn=E2=80=99t=20macOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/src/utils.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index cf18f817..cc3f765c 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -202,6 +202,9 @@ class AccentColorListener { /// Initializes this class. void _init() { + if (kIsWeb) return; + if (!Platform.isMacOS) return; + _initCurrentAccentColor(); _initSystemColorObserver(); } From 9b95a4308bd28c393a1d1c823d3e0676e8a7e721 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:43:46 +0200 Subject: [PATCH 14/94] remove unused imports --- lib/src/buttons/push_button.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index f25f312f..ad2fc9dc 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -1,8 +1,5 @@ // ignore_for_file: prefer_if_null_operators -import 'dart:async'; - -import 'package:appkit_ui_element_colors/appkit_ui_element_colors.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:gradient_borders/gradient_borders.dart'; From 0f52aaae1a0645a0a4eedf31473f7a683c695248 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:44:46 +0200 Subject: [PATCH 15/94] remove reference to deprecated API in documentation --- lib/src/buttons/push_button.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index ad2fc9dc..6752957e 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -197,8 +197,7 @@ class PushButton extends StatefulWidget { /// Whether the button is used as a secondary action button (e.g. Cancel buttons in dialogs) /// - /// Sets its background color to [PushButtonThemeData]'s [secondaryColor] attributes (defaults - /// are gray colors). Can still be overridden if the [color] attribute is non-null. + /// Can still be overridden if the [color] attribute is non-null. final bool? secondary; /// Whether the button is enabled or disabled. Buttons are disabled by default. To From 85fe22f658e5d7e515cd5c011a2fdb65891b8eff Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:46:24 +0200 Subject: [PATCH 16/94] deprecate `pressedOpacity` --- lib/src/buttons/push_button.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index 6752957e..4dfbd92e 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -171,6 +171,8 @@ class PushButton extends StatefulWidget { /// /// This defaults to 0.4. If null, opacity will not change on pressed if using /// your own custom effects is desired. + @Deprecated("'PushButton' now attempts to mimic macOS’ look and feel, " + "therefore, its opacity no longer changes when it is pressed.") final double? pressedOpacity; /// The radius of the button's corners when it has a background color. From 856bfb07df40adba942072dcdb84df24a5f47d61 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:47:58 +0200 Subject: [PATCH 17/94] remove deprecated `pressedOpacity` from `debugFillProperties` --- lib/src/buttons/push_button.dart | 1 - test/buttons/push_button_test.dart | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index 4dfbd92e..a295498b 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -212,7 +212,6 @@ class PushButton extends StatefulWidget { properties.add(EnumProperty('controlSize', controlSize)); properties.add(ColorProperty('color', color)); properties.add(ColorProperty('disabledColor', disabledColor)); - properties.add(DoubleProperty('pressedOpacity', pressedOpacity)); properties.add(DiagnosticsProperty('alignment', alignment)); properties.add(StringProperty('semanticLabel', semanticLabel)); properties.add(DiagnosticsProperty('borderRadius', borderRadius)); diff --git a/test/buttons/push_button_test.dart b/test/buttons/push_button_test.dart index 8bb294a0..981c21df 100644 --- a/test/buttons/push_button_test.dart +++ b/test/buttons/push_button_test.dart @@ -99,7 +99,6 @@ void main() { 'controlSize: regular', 'color: null', 'disabledColor: null', - 'pressedOpacity: 0.4', 'alignment: Alignment.center', 'semanticLabel: null', 'borderRadius: BorderRadius.circular(4.0)', From 3ca7e8149d5d7895744dbeeca76562ec57c25c21 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:50:27 +0200 Subject: [PATCH 18/94] document `AccentColor` --- lib/src/enums/accent_color.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/enums/accent_color.dart b/lib/src/enums/accent_color.dart index ed21cfe8..2090e6f4 100644 --- a/lib/src/enums/accent_color.dart +++ b/lib/src/enums/accent_color.dart @@ -1,4 +1,5 @@ -// TODO: document this +/// The macOS accent color which can be changed by the user in *System Settings* +/// → *Apperance* → *Accent color*. enum AccentColor { blue, purple, From 3c96252bb53efa19830e4a06762da3f7e74769f4 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:51:32 +0200 Subject: [PATCH 19/94] document `AccentColor` values --- lib/src/enums/accent_color.dart | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/src/enums/accent_color.dart b/lib/src/enums/accent_color.dart index 2090e6f4..3e5ecc8e 100644 --- a/lib/src/enums/accent_color.dart +++ b/lib/src/enums/accent_color.dart @@ -1,12 +1,27 @@ /// The macOS accent color which can be changed by the user in *System Settings* /// → *Apperance* → *Accent color*. enum AccentColor { + /// The blue accent color. blue, + + /// The purple accent color. purple, + + /// The pink accent color. pink, + + /// The red accent color. red, + + /// The orange accent color. orange, + + /// The yellow accent color. yellow, + + /// The green accent color. green, + + /// The graphite accent color. graphite, } From 3d06510ed1ca7f0361037769f788366696caaebe Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 15:36:02 +0200 Subject: [PATCH 20/94] increment version number --- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index f642de3a..49e48c7f 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -174,7 +174,7 @@ packages: path: ".." relative: true source: path - version: "2.0.0" + version: "2.0.1" macos_window_utils: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 64a10b1e..d0525137 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 +version: 2.0.1 homepage: "https://macosui.dev" repository: "https://github.com/GroovinChip/macos_ui" From 80bcf9a2f177445069e0f9375ecad21c205e6ac6 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 15:36:16 +0200 Subject: [PATCH 21/94] add changelog entry for version 2.0.1 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c59bae9..e1c43f00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [2.0.1] +* `PushButton` has received a facelift. It now mimics the look of native macOS buttons more closely. + * **Note:** As a result, its `pressedOpacity` property and the `PushButtonTheme` class have been deprecated. + ## [2.0.0] ### 🚨 Breaking Changes 🚨 * `macos_ui` has been migrated to utilize [macos_window_utils](https://pub.dev/packages/macos_window_utils) under the hood, which provides the following benefits: From db6088b6515e9e2bea48b66fdf4e5bac452e0b55 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Fri, 11 Aug 2023 09:31:40 +0200 Subject: [PATCH 22/94] upgrade to macos_window_utils 1.2.0 --- example/pubspec.lock | 4 ++-- pubspec.lock | 4 ++-- pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 49e48c7f..86bd412e 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -179,10 +179,10 @@ packages: dependency: transitive description: name: macos_window_utils - sha256: b78a210aa70ca7ccad6e7b7b810fb4689c507f4a46e299214900b2a1eb70ea23 + sha256: "43a90473f8786f00f07203e6819dab67e032f8896dafa4a6f85fbc71fba32c0b" url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.2.0" matcher: dependency: transitive description: diff --git a/pubspec.lock b/pubspec.lock index 018f3b58..6aa59489 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -215,10 +215,10 @@ packages: dependency: "direct main" description: name: macos_window_utils - sha256: b78a210aa70ca7ccad6e7b7b810fb4689c507f4a46e299214900b2a1eb70ea23 + sha256: "43a90473f8786f00f07203e6819dab67e032f8896dafa4a6f85fbc71fba32c0b" url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.2.0" matcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d0525137..8ffa130b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ environment: dependencies: flutter: sdk: flutter - macos_window_utils: ^1.1.3 + macos_window_utils: ^1.2.0 gradient_borders: ^1.0.0 appkit_ui_element_colors: ^1.0.0 From 2423822ca318b13a779041fb7ed1ee9cd5c543db Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Fri, 11 Aug 2023 09:35:29 +0200 Subject: [PATCH 23/94] initialize `_isWindowMain` properly --- lib/src/utils.dart | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index cc3f765c..19a23909 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -101,9 +101,7 @@ class WindowMainStateListener { NSWindowDelegateHandle? handle; /// Whether the window is currently the main window. - bool _isWindowMain = - true; // TODO: Initialize properly once macos_window_utils supports that, - // see https://github.com/macosui/macos_window_utils.dart/issues/31. + bool _isWindowMain = true; /// Whether the window is currently the main window. bool get isWindowMain => _isWindowMain; @@ -120,6 +118,12 @@ class WindowMainStateListener { if (kIsWeb) return; if (!Platform.isMacOS) return; + _initDelegate(); + _initIsWindowMain(); + } + + /// Initializes the [NSWindowDelegate] to listen for main window changes. + void _initDelegate() { final delegate = _WindowMainStateListenerDelegate( onWindowDidBecomeMain: () { _isWindowMain = true; @@ -133,6 +137,12 @@ class WindowMainStateListener { handle = WindowManipulator.addNSWindowDelegate(delegate); } + /// Initializes the [_isWindowMain] variable. + Future _initIsWindowMain() async { + _isWindowMain = await WindowManipulator.isMainWindow(); + _windowMainStateStreamController.add(_isWindowMain); + } + /// Deinitializes the listener. void deinit() { handle?.removeFromHandler(); From 1ef31a8ba45f2470332d6972375640d11fc16d6e Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Fri, 11 Aug 2023 09:37:18 +0200 Subject: [PATCH 24/94] rename `isWindowMain` to `isMainWindow` --- lib/src/buttons/push_button.dart | 4 ++-- lib/src/utils.dart | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index a295498b..678c8510 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -261,7 +261,7 @@ class PushButtonState extends State BoxDecoration _getBoxDecoration() { // If the window isn’t currently the main window (that is, it is not in // focus), make the button look as if it was a secondary button. - final isWindowMain = WindowMainStateListener.instance.isWindowMain; + final isWindowMain = WindowMainStateListener.instance.isMainWindow; return _BoxDecorationBuilder.buildBoxDecoration( accentColor: _accentColor, @@ -278,7 +278,7 @@ class PushButtonState extends State // If the window isn’t currently the main window (that is, it is not in // focus), make the button look as if it was a secondary button. - final isWindowMain = WindowMainStateListener.instance.isWindowMain; + final isWindowMain = WindowMainStateListener.instance.isMainWindow; return MacosDynamicColor.resolve( widget.color ?? diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 19a23909..3d9a8693 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -101,10 +101,10 @@ class WindowMainStateListener { NSWindowDelegateHandle? handle; /// Whether the window is currently the main window. - bool _isWindowMain = true; + bool _isMainWindow = true; /// Whether the window is currently the main window. - bool get isWindowMain => _isWindowMain; + bool get isMainWindow => _isMainWindow; /// Notifies listeners when the window’s main state changes. final _windowMainStateStreamController = StreamController.broadcast(); @@ -126,21 +126,21 @@ class WindowMainStateListener { void _initDelegate() { final delegate = _WindowMainStateListenerDelegate( onWindowDidBecomeMain: () { - _isWindowMain = true; + _isMainWindow = true; _windowMainStateStreamController.add(true); }, onWindowDidResignMain: () { - _isWindowMain = false; + _isMainWindow = false; _windowMainStateStreamController.add(false); }, ); handle = WindowManipulator.addNSWindowDelegate(delegate); } - /// Initializes the [_isWindowMain] variable. + /// Initializes the [_isMainWindow] variable. Future _initIsWindowMain() async { - _isWindowMain = await WindowManipulator.isMainWindow(); - _windowMainStateStreamController.add(_isWindowMain); + _isMainWindow = await WindowManipulator.isMainWindow(); + _windowMainStateStreamController.add(_isMainWindow); } /// Deinitializes the listener. From f064102d70df5d252b3a478246ac4bf0fdfc1efa Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Fri, 11 Aug 2023 09:39:56 +0200 Subject: [PATCH 25/94] replace `print` with `debugPrint` --- lib/src/utils.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 3d9a8693..470b803f 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -267,8 +267,7 @@ class AccentColorListener { return hueComponentToAccentColor[hueComponent]!; } - // ignore: avoid_print - print( + debugPrint( 'Warning: Falling back on slow accent color resolution. It’s possible ' 'that the accent colors have changed in a recent version of macOS, thus ' 'invalidating macos_ui’s accent colors, which were captured in macOS ' From 9040eb926ae804ed6339765bbaf79219a20f927f Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Fri, 11 Aug 2023 09:50:42 +0200 Subject: [PATCH 26/94] deprecate properties of `PushButtonThemeData` rather than `PushButtonThemeData` itself to avoid breaking changes --- lib/src/theme/macos_theme.dart | 7 ++--- lib/src/theme/push_button_theme.dart | 45 ++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/lib/src/theme/macos_theme.dart b/lib/src/theme/macos_theme.dart index 87405882..0bda4d11 100644 --- a/lib/src/theme/macos_theme.dart +++ b/lib/src/theme/macos_theme.dart @@ -514,8 +514,7 @@ class MacosThemeData with Diagnosticable { typography: MacosTypography.lerp(a.typography, b.typography, t), helpButtonTheme: HelpButtonThemeData.lerp(a.helpButtonTheme, b.helpButtonTheme, t), - pushButtonTheme: - PushButtonThemeData.lerp(a.pushButtonTheme, b.pushButtonTheme, t), + pushButtonTheme: a.pushButtonTheme, tooltipTheme: MacosTooltipThemeData.lerp(a.tooltipTheme, b.tooltipTheme, t), visualDensity: VisualDensity.lerp(a.visualDensity, b.visualDensity, t), @@ -581,7 +580,7 @@ class MacosThemeData with Diagnosticable { canvasColor: canvasColor ?? this.canvasColor, dividerColor: dividerColor ?? this.dividerColor, typography: this.typography.merge(typography), - pushButtonTheme: this.pushButtonTheme.merge(pushButtonTheme), + pushButtonTheme: this.pushButtonTheme, helpButtonTheme: this.helpButtonTheme.merge(helpButtonTheme), tooltipTheme: this.tooltipTheme.merge(tooltipTheme), visualDensity: visualDensity ?? this.visualDensity, @@ -605,7 +604,7 @@ class MacosThemeData with Diagnosticable { canvasColor: other.canvasColor, dividerColor: other.dividerColor, typography: typography.merge(other.typography), - pushButtonTheme: pushButtonTheme.merge(other.pushButtonTheme), + pushButtonTheme: pushButtonTheme, helpButtonTheme: helpButtonTheme.merge(other.helpButtonTheme), tooltipTheme: tooltipTheme.merge(other.tooltipTheme), visualDensity: other.visualDensity, diff --git a/lib/src/theme/push_button_theme.dart b/lib/src/theme/push_button_theme.dart index dc35c2b7..0c69aebd 100644 --- a/lib/src/theme/push_button_theme.dart +++ b/lib/src/theme/push_button_theme.dart @@ -7,12 +7,8 @@ import 'package:macos_ui/src/library.dart'; /// See also: /// /// * [PushButtonThemeData], which is used to configure this theme. -@Deprecated("'PushButton' no longer uses singular colors and therefore cannot " - "be themed using a 'PushButtonTheme'.") class PushButtonTheme extends InheritedTheme { /// Create a [PushButtonTheme]. - /// - /// The [data] parameter must not be null. const PushButtonTheme({ super.key, required this.data, @@ -20,6 +16,9 @@ class PushButtonTheme extends InheritedTheme { }); /// The configuration of this theme. + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") final PushButtonThemeData data; /// The closest instance of this class that encloses the given context. @@ -32,6 +31,9 @@ class PushButtonTheme extends InheritedTheme { /// ```dart /// PushButtonTheme theme = PushButtonTheme.of(context); /// ``` + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") static PushButtonThemeData of(BuildContext context) { final PushButtonTheme? buttonTheme = context.dependOnInheritedWidgetOfExactType(); @@ -39,11 +41,17 @@ class PushButtonTheme extends InheritedTheme { } @override + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") Widget wrap(BuildContext context, Widget child) { return PushButtonTheme(data: data, child: child); } @override + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") bool updateShouldNotify(PushButtonTheme oldWidget) => data != oldWidget.data; } @@ -56,8 +64,6 @@ class PushButtonTheme extends InheritedTheme { /// * [PushButtonTheme], the theme which is configured with this class. /// * [MacosThemeData.pushButtonTheme], which can be used to override the default /// style for [PushButton]s below the overall [MacosTheme]. -@Deprecated("'PushButton' no longer uses singular colors and therefore cannot " - "be themed using a 'PushButtonTheme'.") class PushButtonThemeData with Diagnosticable { /// Creates a [PushButtonThemeData]. const PushButtonThemeData({ @@ -67,15 +73,27 @@ class PushButtonThemeData with Diagnosticable { }); /// The default background color for [PushButton] + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") final Color? color; /// The default disabled color for [PushButton] + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") final Color? disabledColor; /// The default secondary color (e.g. Cancel/Go back buttons) for [PushButton] + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") final Color? secondaryColor; /// Copies this [PushButtonThemeData] into another. + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") PushButtonThemeData copyWith({ Color? color, Color? disabledColor, @@ -91,6 +109,9 @@ class PushButtonThemeData with Diagnosticable { /// Linearly interpolate between two [PushButtonThemeData]. /// /// All the properties must be non-null. + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") static PushButtonThemeData lerp( PushButtonThemeData a, PushButtonThemeData b, @@ -104,6 +125,9 @@ class PushButtonThemeData with Diagnosticable { } @override + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") bool operator ==(Object other) => identical(this, other) || other is PushButtonThemeData && @@ -113,9 +137,15 @@ class PushButtonThemeData with Diagnosticable { secondaryColor?.value == other.secondaryColor?.value; @override + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") int get hashCode => color.hashCode ^ disabledColor.hashCode; @override + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(ColorProperty('color', color)); @@ -124,6 +154,9 @@ class PushButtonThemeData with Diagnosticable { } /// Merges this [PushButtonThemeData] with another. + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") PushButtonThemeData merge(PushButtonThemeData? other) { if (other == null) return this; return copyWith( From 0df03afc4213f2f41e3f4af7a696e5b9fae1e48d Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Fri, 11 Aug 2023 09:51:36 +0200 Subject: [PATCH 27/94] fix typo in `AccentColor` documentation --- lib/src/enums/accent_color.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/enums/accent_color.dart b/lib/src/enums/accent_color.dart index 3e5ecc8e..95ab8ea2 100644 --- a/lib/src/enums/accent_color.dart +++ b/lib/src/enums/accent_color.dart @@ -1,5 +1,5 @@ /// The macOS accent color which can be changed by the user in *System Settings* -/// → *Apperance* → *Accent color*. +/// → *Appearance* → *Accent color*. enum AccentColor { /// The blue accent color. blue, From bc32402fa0ee00f50a9c27ec5f4db20b5bddc98f Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Fri, 11 Aug 2023 09:52:22 +0200 Subject: [PATCH 28/94] fix typo in `AccentColorListener` docs --- lib/src/utils.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 470b803f..5ef5a178 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -207,7 +207,7 @@ class AccentColorListener { /// changes. Stream get onChangedStream => _accentColorStreamController.stream; - /// A stream subcription for the [SystemColorObserver] stream. + /// A stream subscription for the [SystemColorObserver] stream. StreamSubscription? _systemColorObserverStreamSubscription; /// Initializes this class. From ea6a7e571c53d498a73fc2374fe18cc7bdb092e2 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Fri, 4 Aug 2023 19:37:48 +0200 Subject: [PATCH 29/94] automatic changes --- example/macos/Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index 2fa81174..0ee0bdce 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -38,4 +38,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: ff0a9a3ce75ee73f200ca7e2f47745698c917ef9 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.1 From f01d422cacba4d1abdd1be86aa505ca8f2d715f7 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sun, 6 Aug 2023 21:39:25 +0200 Subject: [PATCH 30/94] =?UTF-8?q?imitate=20macOS=E2=80=99=20push=20button?= =?UTF-8?q?=20look?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/pubspec.lock | 8 + lib/src/buttons/push_button.dart | 389 +++++++++++++++++++++++++++++-- lib/src/enums/accent_color.dart | 11 + pubspec.lock | 8 + pubspec.yaml | 1 + 5 files changed, 396 insertions(+), 21 deletions(-) create mode 100644 lib/src/enums/accent_color.dart diff --git a/example/pubspec.lock b/example/pubspec.lock index dc586a2a..eab32583 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -112,6 +112,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.1.0" + gradient_borders: + dependency: transitive + description: + name: gradient_borders + sha256: "69eeaff519d145a4c6c213ada1abae386bcc8981a4970d923e478ce7ba19e309" + url: "https://pub.dev" + source: hosted + version: "1.0.0" http: dependency: transitive description: diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index 19180016..d1fbb238 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -2,7 +2,9 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; +import 'package:gradient_borders/gradient_borders.dart'; import 'package:macos_ui/macos_ui.dart'; +import 'package:macos_ui/src/enums/accent_color.dart'; import 'package:macos_ui/src/library.dart'; const _kMiniButtonSize = Size(26.0, 11.0); @@ -300,29 +302,64 @@ class PushButtonState extends State @visibleForTesting bool buttonHeldDown = false; - @override - Widget build(BuildContext context) { - assert(debugCheckHasMacosTheme(context)); + AccentColor get _accentColor => AccentColor.blue; // TODO: make this dynamic + + BoxDecoration _getBoxDecoration() { + return _BoxDecorationBuilder.buildBoxDecoration( + accentColor: _accentColor, + isEnabled: widget.enabled, + isDarkModeEnabled: MacosTheme.of(context).brightness.isDark, + isSecondary: widget.secondary ?? false, + ); + } + + Color _getBackgroundColor() { final bool enabled = widget.enabled; final bool isSecondary = widget.secondary != null && widget.secondary!; final MacosThemeData theme = MacosTheme.of(context); - final Color backgroundColor = MacosDynamicColor.resolve( + + return MacosDynamicColor.resolve( widget.color ?? - (isSecondary - ? theme.pushButtonTheme.secondaryColor! - : theme.pushButtonTheme.color!), + _BoxDecorationBuilder.getGradientColors( + accentColor: _accentColor, + isEnabled: enabled, + isDarkModeEnabled: theme.brightness.isDark, + isSecondary: isSecondary) + .first, context, ); + } + + Color _getForegroundColor(Color backgroundColor) { + final MacosThemeData theme = MacosTheme.of(context); - final disabledColor = !isSecondary - ? backgroundColor.withOpacity(0.5) - : backgroundColor.withOpacity(0.25); + final blendedBackgroundColor = Color.lerp( + theme.canvasColor, + backgroundColor, + backgroundColor.opacity, + )!; - final Color foregroundColor = widget.enabled - ? textLuminance(backgroundColor) - : theme.brightness.isDark - ? const Color.fromRGBO(255, 255, 255, 0.25) - : const Color.fromRGBO(0, 0, 0, 0.25); + return widget.enabled + ? textLuminance(blendedBackgroundColor) + : textLuminance(blendedBackgroundColor).withOpacity(0.25); + } + + @override + Widget build(BuildContext context) { + assert(debugCheckHasMacosTheme(context)); + final bool enabled = widget.enabled; + final MacosThemeData theme = MacosTheme.of(context); + // TODO: remove this + // final Color backgroundColor = MacosDynamicColor.resolve( + // widget.color ?? + // (isSecondary + // ? theme.pushButtonTheme.secondaryColor! + // : theme.pushButtonTheme.color!), + // context, + // ); + final Color backgroundColor = _getBackgroundColor(); + + final Color foregroundColor = _getForegroundColor(backgroundColor); final baseStyle = theme.typography.body.copyWith(color: foregroundColor); @@ -342,12 +379,8 @@ class PushButtonState extends State child: FadeTransition( opacity: _opacityAnimation, child: DecoratedBox( - decoration: ShapeDecoration( - shape: RoundedRectangleBorder( - borderRadius: widget.controlSize.borderRadius, - ), - // color: !enabled ? disabledColor : backgroundColor, - color: enabled ? backgroundColor : disabledColor, + decoration: _getBoxDecoration().copyWith( + borderRadius: widget.controlSize.borderRadius, ), child: Padding( padding: widget.controlSize.padding, @@ -369,3 +402,317 @@ class PushButtonState extends State ); } } + +class _BoxDecorationBuilder { + /// Gets the colors to use for the [BoxDecoration]’s gradient based on the + /// provided [accentColor], [isEnabled], and [isDarkModeEnabled] properties. + static List getGradientColors({ + required AccentColor accentColor, + required bool isEnabled, + required bool isDarkModeEnabled, + required bool isSecondary, + }) { + final isEnabledFactor = isEnabled ? 1.0 : 0.5; + + if (isSecondary) { + return isDarkModeEnabled + ? [ + MacosColor.fromRGBO(255, 255, 255, 0.251 * isEnabledFactor), + MacosColor.fromRGBO(255, 255, 255, 0.251 * isEnabledFactor), + ] + : [ + MacosColor.fromRGBO(255, 255, 255, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(255, 255, 255, 1.0 * isEnabledFactor), + ]; + } + + if (isDarkModeEnabled) { + switch (accentColor) { + case AccentColor.blue: + return [ + MacosColor.fromRGBO(0, 114, 238, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(0, 94, 211, 1.0 * isEnabledFactor), + ]; + + case AccentColor.purple: + return [ + MacosColor.fromRGBO(135, 65, 131, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(120, 57, 116, 1.0 * isEnabledFactor), + ]; + + case AccentColor.pink: + return [ + MacosColor.fromRGBO(188, 52, 105, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(168, 46, 93, 1.0 * isEnabledFactor), + ]; + + case AccentColor.red: + return [ + MacosColor.fromRGBO(186, 53, 46, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(166, 48, 41, 1.0 * isEnabledFactor), + ]; + + case AccentColor.orange: + return [ + MacosColor.fromRGBO(212, 133, 33, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(189, 118, 30, 1.0 * isEnabledFactor), + ]; + + case AccentColor.yellow: + return [ + MacosColor.fromRGBO(229, 203, 35, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(204, 179, 21, 1.0 * isEnabledFactor), + ]; + + case AccentColor.green: + return [ + MacosColor.fromRGBO(58, 138, 46, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(52, 123, 39, 1.0 * isEnabledFactor), + ]; + + case AccentColor.graphite: + return [ + MacosColor.fromRGBO(64, 64, 64, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(57, 57, 57, 1.0 * isEnabledFactor), + ]; + + default: + throw UnimplementedError(); + } + } else { + switch (accentColor) { + case AccentColor.blue: + return [ + MacosColor.fromRGBO(39, 125, 255, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(1, 101, 255, 1.0 * isEnabledFactor), + ]; + + case AccentColor.purple: + return [ + MacosColor.fromRGBO(148, 73, 143, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(128, 39, 121, 1.0 * isEnabledFactor), + ]; + + case AccentColor.pink: + return [ + MacosColor.fromRGBO(212, 71, 125, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(203, 36, 101, 1.0 * isEnabledFactor), + ]; + + case AccentColor.red: + return [ + MacosColor.fromRGBO(198, 64, 57, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(188, 29, 21, 1.0 * isEnabledFactor), + ]; + + case AccentColor.orange: + return [ + MacosColor.fromRGBO(237, 154, 51, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(234, 136, 13, 1.0 * isEnabledFactor), + ]; + + case AccentColor.yellow: + return [ + MacosColor.fromRGBO(242, 211, 61, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(240, 203, 25, 1.0 * isEnabledFactor), + ]; + + case AccentColor.green: + return [ + MacosColor.fromRGBO(77, 161, 63, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(45, 143, 28, 1.0 * isEnabledFactor), + ]; + + case AccentColor.graphite: + return [ + MacosColor.fromRGBO(86, 86, 86, 1.0 * isEnabledFactor), + MacosColor.fromRGBO(55, 55, 55, 1.0 * isEnabledFactor), + ]; + + default: + throw UnimplementedError(); + } + } + } + + /// Gets the shadow to use for the [BoxDecoration] based on the provided + /// [accentColor], [isEnabled], and [isDarkModeEnabled] properties. + static List _getShadow({ + required AccentColor accentColor, + required bool isEnabled, + required bool isDarkModeEnabled, + required bool isSecondary, + }) { + final isEnabledFactor = isEnabled ? 1.0 : 0.5; + + if (isSecondary) { + return isDarkModeEnabled + ? [ + BoxShadow( + color: MacosColor.fromRGBO(0, 0, 0, 0.4 * isEnabledFactor), + blurRadius: 0.5, + offset: Offset.zero, + spreadRadius: 0.0, + blurStyle: BlurStyle.outer, + ), + ] + : [ + BoxShadow( + color: MacosColor.fromRGBO(0, 0, 0, 0.4 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + } + + if (isDarkModeEnabled) { + return [ + BoxShadow( + color: MacosColor.fromRGBO(0, 0, 0, 0.4 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + } else { + switch (accentColor) { + case AccentColor.blue: + return [ + BoxShadow( + color: MacosColor.fromRGBO(0, 103, 255, 0.21 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + + case AccentColor.purple: + return [ + BoxShadow( + color: MacosColor.fromRGBO(139, 29, 125, 0.21 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + + case AccentColor.pink: + return [ + BoxShadow( + color: MacosColor.fromRGBO(222, 0, 101, 0.21 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + + case AccentColor.red: + return [ + BoxShadow( + color: MacosColor.fromRGBO(188, 29, 21, 0.35 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + + case AccentColor.orange: + return [ + BoxShadow( + color: MacosColor.fromRGBO(234, 136, 13, 0.35 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + + case AccentColor.yellow: + return [ + BoxShadow( + color: MacosColor.fromRGBO(240, 203, 25, 0.35 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + + case AccentColor.green: + return [ + BoxShadow( + color: MacosColor.fromRGBO(45, 143, 28, 0.35 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + + case AccentColor.graphite: + return [ + BoxShadow( + color: MacosColor.fromRGBO(55, 55, 55, 0.35 * isEnabledFactor), + blurRadius: 0.5, + offset: isEnabled ? const Offset(0.0, 0.3) : Offset.zero, + spreadRadius: 0.0, + blurStyle: isEnabled ? BlurStyle.normal : BlurStyle.outer, + ), + ]; + + default: + throw UnimplementedError(); + } + } + } + + /// Builds a [BoxDecoration] for a [MacosPushButton]. + static BoxDecoration buildBoxDecoration({ + required AccentColor accentColor, + required bool isEnabled, + required bool isDarkModeEnabled, + required bool isSecondary, + }) { + final isEnabledFactor = isEnabled ? 1.0 : 0.5; + + return BoxDecoration( + border: isDarkModeEnabled + ? GradientBoxBorder( + gradient: LinearGradient( + colors: [ + MacosColor.fromRGBO(255, 255, 255, 0.43 * isEnabledFactor), + const MacosColor.fromRGBO(255, 255, 255, 0.0), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + stops: const [0.0, 0.2], + ), + width: 0.7, + ) + : null, + gradient: LinearGradient( + colors: getGradientColors( + accentColor: accentColor, + isEnabled: isEnabled, + isDarkModeEnabled: isDarkModeEnabled, + isSecondary: isSecondary, + ), + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + boxShadow: _getShadow( + accentColor: accentColor, + isEnabled: isEnabled, + isDarkModeEnabled: isDarkModeEnabled, + isSecondary: isSecondary, + ), + ); + } +} diff --git a/lib/src/enums/accent_color.dart b/lib/src/enums/accent_color.dart new file mode 100644 index 00000000..ed21cfe8 --- /dev/null +++ b/lib/src/enums/accent_color.dart @@ -0,0 +1,11 @@ +// TODO: document this +enum AccentColor { + blue, + purple, + pink, + red, + orange, + yellow, + green, + graphite, +} diff --git a/pubspec.lock b/pubspec.lock index dfa8a61c..be900083 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -139,6 +139,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + gradient_borders: + dependency: "direct main" + description: + name: gradient_borders + sha256: "69eeaff519d145a4c6c213ada1abae386bcc8981a4970d923e478ce7ba19e309" + url: "https://pub.dev" + source: hosted + version: "1.0.0" http_multi_server: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 13865b31..7b208ea5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,6 +12,7 @@ dependencies: flutter: sdk: flutter macos_window_utils: ^1.1.3 + gradient_borders: ^1.0.0 dev_dependencies: flutter_test: From d0255e16f648de4004e2b4b3e215a64c2b8ffe8b Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sun, 6 Aug 2023 22:51:54 +0200 Subject: [PATCH 31/94] make push button appear secondary when window resigns main state --- lib/src/buttons/push_button.dart | 15 ++++++- lib/src/utils.dart | 72 ++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index d1fbb238..3aa9cfde 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -1,5 +1,7 @@ // ignore_for_file: prefer_if_null_operators +import 'dart:async'; + import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:gradient_borders/gradient_borders.dart'; @@ -237,6 +239,8 @@ class PushButtonState extends State late AnimationController _animationController; late Animation _opacityAnimation; + late StreamSubscription _onWindowMainStateChangedStreamSubcription; + @override void initState() { super.initState(); @@ -249,6 +253,10 @@ class PushButtonState extends State .drive(CurveTween(curve: Curves.decelerate)) .drive(_opacityTween); _setTween(); + + _onWindowMainStateChangedStreamSubcription = WindowMainStateListener + .instance.onChangedStream + .listen((_) => setState(() {})); } @override @@ -296,6 +304,7 @@ class PushButtonState extends State @override void dispose() { _animationController.dispose(); + _onWindowMainStateChangedStreamSubcription.cancel(); super.dispose(); } @@ -305,11 +314,15 @@ class PushButtonState extends State AccentColor get _accentColor => AccentColor.blue; // TODO: make this dynamic BoxDecoration _getBoxDecoration() { + // If the window isn’t currently the main window (that is, it is not in + // focus), make the button look as if it was a secondary button. + final isWindowMain = WindowMainStateListener.instance.isWindowMain; + return _BoxDecorationBuilder.buildBoxDecoration( accentColor: _accentColor, isEnabled: widget.enabled, isDarkModeEnabled: MacosTheme.of(context).brightness.isDark, - isSecondary: widget.secondary ?? false, + isSecondary: !isWindowMain || (widget.secondary ?? false), ); } diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 1f2d8a1e..dcb8212a 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; @@ -86,3 +87,74 @@ class MacOSBrightnessOverrideHandler { _lastBrightness = currentBrightness; } } + +/// A class that listens for changes to the application's window being the main +/// window, and notifies listeners. +class WindowMainStateListener { + /// A shared instance of [WindowMainStateListener]. + static final instance = WindowMainStateListener(); + + /// A [NSWindowDelegateHandle], to be used when deiniting the listener. + NSWindowDelegateHandle? handle; + + /// Whether the window is currently the main window. + bool _isWindowMain = + true; // TODO: Initialize properly once macos_window_utils supports that, + // see https://github.com/macosui/macos_window_utils.dart/issues/31. + + /// Whether the window is currently the main window. + bool get isWindowMain => _isWindowMain; + + /// Notifies listeners when the window’s main state changes. + final _windowMainStateStreamController = StreamController.broadcast(); + + /// A stream of the window’s main state. Emits a new value whenever the state + /// changes. + Stream get onChangedStream => _windowMainStateStreamController.stream; + + /// Initializes the listener. This should only be called once. + void _init() { + final delegate = _WindowMainStateListenerDelegate( + onWindowDidBecomeMain: () { + _isWindowMain = true; + _windowMainStateStreamController.add(true); + }, + onWindowDidResignMain: () { + _isWindowMain = false; + _windowMainStateStreamController.add(false); + }, + ); + handle = WindowManipulator.addNSWindowDelegate(delegate); + } + + /// Deinitializes the listener. + void deinit() { + handle?.removeFromHandler(); + } + + /// A class that listens for changes to the application's window being the + /// main window, and notifies listeners. + WindowMainStateListener() { + _init(); + } +} + +/// The [NSWindowDelegate] used by [WindowMainStateListener]. +class _WindowMainStateListenerDelegate extends NSWindowDelegate { + _WindowMainStateListenerDelegate({ + required this.onWindowDidBecomeMain, + required this.onWindowDidResignMain, + }); + + /// Called when the window becomes the main window. + final void Function() onWindowDidBecomeMain; + + /// Called when the window resigns as the main window. + final void Function() onWindowDidResignMain; + + @override + void windowDidBecomeMain() => onWindowDidBecomeMain(); + + @override + void windowDidResignMain() => onWindowDidResignMain(); +} From 5e905194c64b19d73c0a46edf5e252faf47aa5dd Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sun, 6 Aug 2023 22:58:36 +0200 Subject: [PATCH 32/94] migrate to stream builder --- lib/src/buttons/push_button.dart | 45 +++++++++++++++----------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index 3aa9cfde..ad7be046 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -239,8 +239,6 @@ class PushButtonState extends State late AnimationController _animationController; late Animation _opacityAnimation; - late StreamSubscription _onWindowMainStateChangedStreamSubcription; - @override void initState() { super.initState(); @@ -253,10 +251,6 @@ class PushButtonState extends State .drive(CurveTween(curve: Curves.decelerate)) .drive(_opacityTween); _setTween(); - - _onWindowMainStateChangedStreamSubcription = WindowMainStateListener - .instance.onChangedStream - .listen((_) => setState(() {})); } @override @@ -304,7 +298,6 @@ class PushButtonState extends State @override void dispose() { _animationController.dispose(); - _onWindowMainStateChangedStreamSubcription.cancel(); super.dispose(); } @@ -391,23 +384,27 @@ class PushButtonState extends State constraints: widget.controlSize.constraints, child: FadeTransition( opacity: _opacityAnimation, - child: DecoratedBox( - decoration: _getBoxDecoration().copyWith( - borderRadius: widget.controlSize.borderRadius, - ), - child: Padding( - padding: widget.controlSize.padding, - child: Align( - alignment: widget.alignment, - widthFactor: 1.0, - heightFactor: 1.0, - child: DefaultTextStyle( - style: widget.controlSize.textStyle(baseStyle), - child: widget.child, - ), - ), - ), - ), + child: StreamBuilder( + stream: WindowMainStateListener.instance.onChangedStream, + builder: (context, _) { + return DecoratedBox( + decoration: _getBoxDecoration().copyWith( + borderRadius: widget.controlSize.borderRadius, + ), + child: Padding( + padding: widget.controlSize.padding, + child: Align( + alignment: widget.alignment, + widthFactor: 1.0, + heightFactor: 1.0, + child: DefaultTextStyle( + style: widget.controlSize.textStyle(baseStyle), + child: widget.child, + ), + ), + ), + ); + }), ), ), ), From 08727e099fe85b0bf8d9c022d9d22f2ca8c86a01 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sun, 6 Aug 2023 23:05:38 +0200 Subject: [PATCH 33/94] fix incorrect text color in push button when window resigns main state --- lib/src/buttons/push_button.dart | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index ad7be046..1979204b 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -324,14 +324,18 @@ class PushButtonState extends State final bool isSecondary = widget.secondary != null && widget.secondary!; final MacosThemeData theme = MacosTheme.of(context); + // If the window isn’t currently the main window (that is, it is not in + // focus), make the button look as if it was a secondary button. + final isWindowMain = WindowMainStateListener.instance.isWindowMain; + return MacosDynamicColor.resolve( widget.color ?? _BoxDecorationBuilder.getGradientColors( - accentColor: _accentColor, - isEnabled: enabled, - isDarkModeEnabled: theme.brightness.isDark, - isSecondary: isSecondary) - .first, + accentColor: _accentColor, + isEnabled: enabled, + isDarkModeEnabled: theme.brightness.isDark, + isSecondary: isSecondary || !isWindowMain, + ).first, context, ); } @@ -363,11 +367,6 @@ class PushButtonState extends State // : theme.pushButtonTheme.color!), // context, // ); - final Color backgroundColor = _getBackgroundColor(); - - final Color foregroundColor = _getForegroundColor(backgroundColor); - - final baseStyle = theme.typography.body.copyWith(color: foregroundColor); return MouseRegion( cursor: widget.mouseCursor!, @@ -387,6 +386,14 @@ class PushButtonState extends State child: StreamBuilder( stream: WindowMainStateListener.instance.onChangedStream, builder: (context, _) { + final Color backgroundColor = _getBackgroundColor(); + + final Color foregroundColor = + _getForegroundColor(backgroundColor); + + final baseStyle = + theme.typography.body.copyWith(color: foregroundColor); + return DecoratedBox( decoration: _getBoxDecoration().copyWith( borderRadius: widget.controlSize.borderRadius, From 0c75ce2fb737f08164a4147dbf42f5e7bff22f24 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 12:27:17 +0200 Subject: [PATCH 34/94] add missing comma --- lib/src/buttons/push_button.dart | 53 ++++++++++++++++---------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index 1979204b..2f714bb6 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -384,34 +384,35 @@ class PushButtonState extends State child: FadeTransition( opacity: _opacityAnimation, child: StreamBuilder( - stream: WindowMainStateListener.instance.onChangedStream, - builder: (context, _) { - final Color backgroundColor = _getBackgroundColor(); - - final Color foregroundColor = - _getForegroundColor(backgroundColor); - - final baseStyle = - theme.typography.body.copyWith(color: foregroundColor); - - return DecoratedBox( - decoration: _getBoxDecoration().copyWith( - borderRadius: widget.controlSize.borderRadius, - ), - child: Padding( - padding: widget.controlSize.padding, - child: Align( - alignment: widget.alignment, - widthFactor: 1.0, - heightFactor: 1.0, - child: DefaultTextStyle( - style: widget.controlSize.textStyle(baseStyle), - child: widget.child, - ), + stream: WindowMainStateListener.instance.onChangedStream, + builder: (context, _) { + final Color backgroundColor = _getBackgroundColor(); + + final Color foregroundColor = + _getForegroundColor(backgroundColor); + + final baseStyle = + theme.typography.body.copyWith(color: foregroundColor); + + return DecoratedBox( + decoration: _getBoxDecoration().copyWith( + borderRadius: widget.controlSize.borderRadius, + ), + child: Padding( + padding: widget.controlSize.padding, + child: Align( + alignment: widget.alignment, + widthFactor: 1.0, + heightFactor: 1.0, + child: DefaultTextStyle( + style: widget.controlSize.textStyle(baseStyle), + child: widget.child, ), ), - ); - }), + ), + ); + }, + ), ), ), ), From 7e9e60271df740f91f25491d1903ab76ebd49a09 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:02:36 +0200 Subject: [PATCH 35/94] implement `AccentColorListener` --- .../Flutter/GeneratedPluginRegistrant.swift | 2 + example/macos/Podfile.lock | 6 + example/pubspec.lock | 16 +++ lib/src/utils.dart | 133 ++++++++++++++++++ pubspec.lock | 24 ++++ pubspec.yaml | 1 + 6 files changed, 182 insertions(+) diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift index 0179c12c..1e64f18d 100644 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,12 +5,14 @@ import FlutterMacOS import Foundation +import appkit_ui_element_colors import macos_ui import macos_window_utils import path_provider_foundation import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + AppkitUiElementColorsPlugin.register(with: registry.registrar(forPlugin: "AppkitUiElementColorsPlugin")) MacOSUiPlugin.register(with: registry.registrar(forPlugin: "MacOSUiPlugin")) MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index 0ee0bdce..6aa1d86e 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -1,4 +1,6 @@ PODS: + - appkit_ui_element_colors (1.0.0): + - FlutterMacOS - FlutterMacOS (1.0.0) - macos_ui (0.1.0): - FlutterMacOS @@ -11,6 +13,7 @@ PODS: - FlutterMacOS DEPENDENCIES: + - appkit_ui_element_colors (from `Flutter/ephemeral/.symlinks/plugins/appkit_ui_element_colors/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - macos_ui (from `Flutter/ephemeral/.symlinks/plugins/macos_ui/macos`) - macos_window_utils (from `Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos`) @@ -18,6 +21,8 @@ DEPENDENCIES: - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) EXTERNAL SOURCES: + appkit_ui_element_colors: + :path: Flutter/ephemeral/.symlinks/plugins/appkit_ui_element_colors/macos FlutterMacOS: :path: Flutter/ephemeral macos_ui: @@ -30,6 +35,7 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos SPEC CHECKSUMS: + appkit_ui_element_colors: 39bb2d80be3f19b152ccf4c70d5bbe6cba43d74a FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 macos_ui: 6229a8922cd97bafb7d9636c8eb8dfb0744183ca macos_window_utils: 933f91f64805e2eb91a5bd057cf97cd097276663 diff --git a/example/pubspec.lock b/example/pubspec.lock index eab32583..f642de3a 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + appkit_ui_element_colors: + dependency: transitive + description: + name: appkit_ui_element_colors + sha256: c3e50f900aae314d339de489535736238627071457c4a4a2dbbb1545b4f04f22 + url: "https://pub.dev" + source: hosted + version: "1.0.0" async: dependency: transitive description: @@ -57,6 +65,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" + equatable: + dependency: transitive + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" fake_async: dependency: transitive description: diff --git a/lib/src/utils.dart b/lib/src/utils.dart index dcb8212a..d71b8cd4 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -1,8 +1,11 @@ import 'dart:async'; import 'dart:io'; +import 'dart:math'; +import 'package:appkit_ui_element_colors/appkit_ui_element_colors.dart'; import 'package:flutter/foundation.dart'; import 'package:macos_ui/macos_ui.dart'; +import 'package:macos_ui/src/enums/accent_color.dart'; import 'package:macos_ui/src/library.dart'; /// Asserts that the given context has a [MacosTheme] ancestor. @@ -158,3 +161,133 @@ class _WindowMainStateListenerDelegate extends NSWindowDelegate { @override void windowDidResignMain() => onWindowDidResignMain(); } + +/// A class that listens to accent color changes. +class AccentColorListener { + /// A shared instance of [AccentColorListener]. + static final instance = AccentColorListener(); + + /// A map which maps hue components of the [UiElementColor.controlAccentColor] + /// color captured with the [NSAppearanceName.aqua] appearance in the + /// [NSColorSpace.genericRGB] color space to the corresponding [AccentColor]. + static final hueComponentToAccentColor = { + 0.6085324903200698: AccentColor.blue, + 0.8285987697113538: AccentColor.purple, + 0.9209523937489168: AccentColor.pink, + 0.9861913496946438: AccentColor.red, + 0.06543037411201169: AccentColor.orange, + 0.11813830353929083: AccentColor.yellow, + 0.29428158007138466: AccentColor.green, + 0.0: AccentColor.graphite, + }; + + /// The currently active accent color. + AccentColor? _currentAccentColor; + + /// The currently active accent color. + AccentColor? get currentAccentColor => _currentAccentColor; + + /// Notifies listeners when the accent color changes. + final _accentColorStreamController = StreamController.broadcast(); + + /// An accent color stream. Emits a new value whenever the accent color + /// changes. + Stream get onChangedStream => _accentColorStreamController.stream; + + /// A stream subcription for the [SystemColorObserver] stream. + StreamSubscription? _systemColorObserverStreamSubscription; + + /// Initializes this class. + void _init() { + _initCurrentAccentColor(); + _initSystemColorObserver(); + } + + /// Deinitializes this class. + void deinit() { + _systemColorObserverStreamSubscription?.cancel(); + } + + /// Initializes the current accent color. This method is to be called whenever + /// a change is detected. + Future _initCurrentAccentColor() async { + final hueComponent = await _getHueComponent(); + _currentAccentColor = _resolveAccentColorFromHueComponent(hueComponent); + _accentColorStreamController.add(null); + } + + /// Initializes the current system color observer. This method may only be + /// called once. + void _initSystemColorObserver() { + assert(_systemColorObserverStreamSubscription == null); + + _systemColorObserverStreamSubscription = + AppkitUiElementColors.systemColorObserver.stream.listen((_) { + _initCurrentAccentColor(); + _accentColorStreamController.add(null); + }); + } + + /// Returns the hue component of the currently active accent color on macOS. + Future _getHueComponent() async { + final color = await AppkitUiElementColors.getColorComponents( + uiElementColor: UiElementColor.controlAccentColor, + components: const { + NSColorComponent.hueComponent, + }, + colorSpace: NSColorSpace.genericRGB, + appearance: NSAppearanceName.aqua, + ); + + assert(color.containsKey("hueComponent")); + + return color["hueComponent"]!; + } + + /// Returns the [AccentColor] which corresponds to the provided + /// [hueComponent]. + AccentColor _resolveAccentColorFromHueComponent(double hueComponent) { + if (hueComponentToAccentColor.containsKey(hueComponent)) { + return hueComponentToAccentColor[hueComponent]!; + } + + // ignore: avoid_print + print( + 'Warning: Falling back on slow accent color resolution. It’s possible ' + 'that the accent colors have changed in a recent version of macOS, thus ' + 'invalidating macos_ui’s accent colors, which were captured in macOS ' + 'Ventura. If you see this message, please notify a maintainer of the ' + 'macos_ui package.', + ); + + return _slowlyResolveAccentColorFromHueComponent(hueComponent); + } + + /// This is a fallback method in case the above method fails. + AccentColor _slowlyResolveAccentColorFromHueComponent(double hueComponent) { + final entries = hueComponentToAccentColor.entries; + var lowestDistance = double.maxFinite; + var toBeReturnedAccentColor = AccentColor.values.first; + + for (var entry in entries) { + final distance = _distanceBetweenHueComponents(hueComponent, entry.key); + if (distance < lowestDistance) { + lowestDistance = distance; + toBeReturnedAccentColor = entry.value; + } + } + + return toBeReturnedAccentColor; + } + + /// Returns the distance between two hue components. + double _distanceBetweenHueComponents(double component1, double component2) { + final rawDifference = (component1 - component2).abs(); + return sin(rawDifference * pi); + } + + /// A class that listens to accent color changes. + AccentColorListener() { + _init(); + } +} diff --git a/pubspec.lock b/pubspec.lock index be900083..018f3b58 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.13.0" + appkit_ui_element_colors: + dependency: "direct main" + description: + name: appkit_ui_element_colors + sha256: c3e50f900aae314d339de489535736238627071457c4a4a2dbbb1545b4f04f22 + url: "https://pub.dev" + source: hosted + version: "1.0.0" args: dependency: transitive description: @@ -89,6 +97,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" + equatable: + dependency: transitive + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" fake_async: dependency: transitive description: @@ -267,6 +283,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.3" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" pool: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 7b208ea5..64a10b1e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,6 +13,7 @@ dependencies: sdk: flutter macos_window_utils: ^1.1.3 gradient_borders: ^1.0.0 + appkit_ui_element_colors: ^1.0.0 dev_dependencies: flutter_test: From 6a31e1caee028177e60ab65f129aca67669744da Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:02:47 +0200 Subject: [PATCH 36/94] make push button listen to accent color changes --- lib/src/buttons/push_button.dart | 68 +++++++++++++++++--------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index 2f714bb6..27b7d4d5 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -2,6 +2,7 @@ import 'dart:async'; +import 'package:appkit_ui_element_colors/appkit_ui_element_colors.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:gradient_borders/gradient_borders.dart'; @@ -304,7 +305,8 @@ class PushButtonState extends State @visibleForTesting bool buttonHeldDown = false; - AccentColor get _accentColor => AccentColor.blue; // TODO: make this dynamic + AccentColor get _accentColor => + AccentColorListener.instance.currentAccentColor ?? AccentColor.blue; BoxDecoration _getBoxDecoration() { // If the window isn’t currently the main window (that is, it is not in @@ -383,36 +385,40 @@ class PushButtonState extends State constraints: widget.controlSize.constraints, child: FadeTransition( opacity: _opacityAnimation, - child: StreamBuilder( - stream: WindowMainStateListener.instance.onChangedStream, - builder: (context, _) { - final Color backgroundColor = _getBackgroundColor(); - - final Color foregroundColor = - _getForegroundColor(backgroundColor); - - final baseStyle = - theme.typography.body.copyWith(color: foregroundColor); - - return DecoratedBox( - decoration: _getBoxDecoration().copyWith( - borderRadius: widget.controlSize.borderRadius, - ), - child: Padding( - padding: widget.controlSize.padding, - child: Align( - alignment: widget.alignment, - widthFactor: 1.0, - heightFactor: 1.0, - child: DefaultTextStyle( - style: widget.controlSize.textStyle(baseStyle), - child: widget.child, - ), - ), - ), - ); - }, - ), + child: StreamBuilder( + stream: AccentColorListener.instance.onChangedStream, + builder: (context, _) { + return StreamBuilder( + stream: WindowMainStateListener.instance.onChangedStream, + builder: (context, _) { + final Color backgroundColor = _getBackgroundColor(); + + final Color foregroundColor = + _getForegroundColor(backgroundColor); + + final baseStyle = theme.typography.body + .copyWith(color: foregroundColor); + + return DecoratedBox( + decoration: _getBoxDecoration().copyWith( + borderRadius: widget.controlSize.borderRadius, + ), + child: Padding( + padding: widget.controlSize.padding, + child: Align( + alignment: widget.alignment, + widthFactor: 1.0, + heightFactor: 1.0, + child: DefaultTextStyle( + style: widget.controlSize.textStyle(baseStyle), + child: widget.child, + ), + ), + ), + ); + }, + ); + }), ), ), ), From 3eaab39006c09d59cb1ef4cd260762ed4b4d2aa2 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:03:03 +0200 Subject: [PATCH 37/94] add missing comma --- lib/src/buttons/push_button.dart | 63 ++++++++++++++++---------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index 27b7d4d5..6bea8db5 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -386,39 +386,40 @@ class PushButtonState extends State child: FadeTransition( opacity: _opacityAnimation, child: StreamBuilder( - stream: AccentColorListener.instance.onChangedStream, - builder: (context, _) { - return StreamBuilder( - stream: WindowMainStateListener.instance.onChangedStream, - builder: (context, _) { - final Color backgroundColor = _getBackgroundColor(); - - final Color foregroundColor = - _getForegroundColor(backgroundColor); - - final baseStyle = theme.typography.body - .copyWith(color: foregroundColor); - - return DecoratedBox( - decoration: _getBoxDecoration().copyWith( - borderRadius: widget.controlSize.borderRadius, - ), - child: Padding( - padding: widget.controlSize.padding, - child: Align( - alignment: widget.alignment, - widthFactor: 1.0, - heightFactor: 1.0, - child: DefaultTextStyle( - style: widget.controlSize.textStyle(baseStyle), - child: widget.child, - ), + stream: AccentColorListener.instance.onChangedStream, + builder: (context, _) { + return StreamBuilder( + stream: WindowMainStateListener.instance.onChangedStream, + builder: (context, _) { + final Color backgroundColor = _getBackgroundColor(); + + final Color foregroundColor = + _getForegroundColor(backgroundColor); + + final baseStyle = theme.typography.body + .copyWith(color: foregroundColor); + + return DecoratedBox( + decoration: _getBoxDecoration().copyWith( + borderRadius: widget.controlSize.borderRadius, + ), + child: Padding( + padding: widget.controlSize.padding, + child: Align( + alignment: widget.alignment, + widthFactor: 1.0, + heightFactor: 1.0, + child: DefaultTextStyle( + style: widget.controlSize.textStyle(baseStyle), + child: widget.child, ), ), - ); - }, - ); - }), + ), + ); + }, + ); + }, + ), ), ), ), From 5d230a58c51f25a280f6a307805abb22f7856b2b Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:09:07 +0200 Subject: [PATCH 38/94] deprecate `PushButtonTheme` --- lib/src/buttons/push_button.dart | 8 -------- lib/src/theme/push_button_theme.dart | 4 ++++ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index 6bea8db5..e37d8828 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -361,14 +361,6 @@ class PushButtonState extends State assert(debugCheckHasMacosTheme(context)); final bool enabled = widget.enabled; final MacosThemeData theme = MacosTheme.of(context); - // TODO: remove this - // final Color backgroundColor = MacosDynamicColor.resolve( - // widget.color ?? - // (isSecondary - // ? theme.pushButtonTheme.secondaryColor! - // : theme.pushButtonTheme.color!), - // context, - // ); return MouseRegion( cursor: widget.mouseCursor!, diff --git a/lib/src/theme/push_button_theme.dart b/lib/src/theme/push_button_theme.dart index c7d27039..dc35c2b7 100644 --- a/lib/src/theme/push_button_theme.dart +++ b/lib/src/theme/push_button_theme.dart @@ -7,6 +7,8 @@ import 'package:macos_ui/src/library.dart'; /// See also: /// /// * [PushButtonThemeData], which is used to configure this theme. +@Deprecated("'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") class PushButtonTheme extends InheritedTheme { /// Create a [PushButtonTheme]. /// @@ -54,6 +56,8 @@ class PushButtonTheme extends InheritedTheme { /// * [PushButtonTheme], the theme which is configured with this class. /// * [MacosThemeData.pushButtonTheme], which can be used to override the default /// style for [PushButton]s below the overall [MacosTheme]. +@Deprecated("'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") class PushButtonThemeData with Diagnosticable { /// Creates a [PushButtonThemeData]. const PushButtonThemeData({ From c99b3f00bddda3d3a0946faeb18d5493063841b6 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:39:58 +0200 Subject: [PATCH 39/94] =?UTF-8?q?mimic=20macOS=E2=80=99=20push=20button=20?= =?UTF-8?q?click=20effect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/src/buttons/push_button.dart | 119 ++++++++++++------------------- 1 file changed, 44 insertions(+), 75 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index e37d8828..f25f312f 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -232,76 +232,29 @@ class PushButton extends StatefulWidget { class PushButtonState extends State with SingleTickerProviderStateMixin { - // Eyeballed values. Feel free to tweak. - static const Duration kFadeOutDuration = Duration(milliseconds: 10); - static const Duration kFadeInDuration = Duration(milliseconds: 100); - final Tween _opacityTween = Tween(begin: 1.0); - - late AnimationController _animationController; - late Animation _opacityAnimation; - - @override - void initState() { - super.initState(); - _animationController = AnimationController( - duration: const Duration(milliseconds: 200), - value: 0.0, - vsync: this, - ); - _opacityAnimation = _animationController - .drive(CurveTween(curve: Curves.decelerate)) - .drive(_opacityTween); - _setTween(); - } - @override void didUpdateWidget(PushButton oldWidget) { super.didUpdateWidget(oldWidget); - _setTween(); - } - - void _setTween() { - _opacityTween.end = widget.pressedOpacity ?? 1.0; } void _handleTapDown(TapDownDetails event) { if (!buttonHeldDown) { - buttonHeldDown = true; - _animate(); + setState(() => buttonHeldDown = true); } } void _handleTapUp(TapUpDetails event) { if (buttonHeldDown) { - buttonHeldDown = false; - _animate(); + setState(() => buttonHeldDown = false); } } void _handleTapCancel() { if (buttonHeldDown) { - buttonHeldDown = false; - _animate(); + setState(() => buttonHeldDown = false); } } - void _animate() { - if (_animationController.isAnimating) return; - final bool wasHeldDown = buttonHeldDown; - final TickerFuture ticker = buttonHeldDown - ? _animationController.animateTo(1.0, duration: kFadeOutDuration) - : _animationController.animateTo(0.0, duration: kFadeInDuration); - ticker.then((void value) { - if (mounted && wasHeldDown != buttonHeldDown) _animate(); - }); - } - - @override - void dispose() { - _animationController.dispose(); - super.dispose(); - } - @visibleForTesting bool buttonHeldDown = false; @@ -356,6 +309,20 @@ class PushButtonState extends State : textLuminance(blendedBackgroundColor).withOpacity(0.25); } + BoxDecoration _getClickEffectBoxDecoration() { + final MacosThemeData theme = MacosTheme.of(context); + final isDark = theme.brightness.isDark; + + final color = isDark + ? const MacosColor.fromRGBO(255, 255, 255, 0.15) + : const MacosColor.fromRGBO(0, 0, 0, 0.06); + + return BoxDecoration( + color: color, + borderRadius: widget.controlSize.borderRadius, + ); + } + @override Widget build(BuildContext context) { assert(debugCheckHasMacosTheme(context)); @@ -375,26 +342,28 @@ class PushButtonState extends State label: widget.semanticLabel, child: ConstrainedBox( constraints: widget.controlSize.constraints, - child: FadeTransition( - opacity: _opacityAnimation, - child: StreamBuilder( - stream: AccentColorListener.instance.onChangedStream, - builder: (context, _) { - return StreamBuilder( - stream: WindowMainStateListener.instance.onChangedStream, - builder: (context, _) { - final Color backgroundColor = _getBackgroundColor(); - - final Color foregroundColor = - _getForegroundColor(backgroundColor); - - final baseStyle = theme.typography.body - .copyWith(color: foregroundColor); - - return DecoratedBox( - decoration: _getBoxDecoration().copyWith( - borderRadius: widget.controlSize.borderRadius, - ), + child: StreamBuilder( + stream: AccentColorListener.instance.onChangedStream, + builder: (context, _) { + return StreamBuilder( + stream: WindowMainStateListener.instance.onChangedStream, + builder: (context, _) { + final Color backgroundColor = _getBackgroundColor(); + + final Color foregroundColor = + _getForegroundColor(backgroundColor); + + final baseStyle = + theme.typography.body.copyWith(color: foregroundColor); + + return DecoratedBox( + decoration: _getBoxDecoration().copyWith( + borderRadius: widget.controlSize.borderRadius, + ), + child: Container( + foregroundDecoration: buttonHeldDown + ? _getClickEffectBoxDecoration() + : const BoxDecoration(), child: Padding( padding: widget.controlSize.padding, child: Align( @@ -407,11 +376,11 @@ class PushButtonState extends State ), ), ), - ); - }, - ); - }, - ), + ), + ); + }, + ); + }, ), ), ), From 8196267860286f6a030216122acba4683109ef78 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:42:05 +0200 Subject: [PATCH 40/94] =?UTF-8?q?do=20not=20init=20`WindowMainStateListene?= =?UTF-8?q?r`=20if=20platform=20isn=E2=80=99t=20macOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/src/utils.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index d71b8cd4..cf18f817 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -117,6 +117,9 @@ class WindowMainStateListener { /// Initializes the listener. This should only be called once. void _init() { + if (kIsWeb) return; + if (!Platform.isMacOS) return; + final delegate = _WindowMainStateListenerDelegate( onWindowDidBecomeMain: () { _isWindowMain = true; From 555bae678c34cfcb772fa8b49bc378f0182810a0 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:42:27 +0200 Subject: [PATCH 41/94] =?UTF-8?q?do=20not=20init=20`AccentColorListener`?= =?UTF-8?q?=20if=20platform=20isn=E2=80=99t=20macOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/src/utils.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index cf18f817..cc3f765c 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -202,6 +202,9 @@ class AccentColorListener { /// Initializes this class. void _init() { + if (kIsWeb) return; + if (!Platform.isMacOS) return; + _initCurrentAccentColor(); _initSystemColorObserver(); } From b8b22d7da7d79775f573a7cb4310a90af9e1ee4b Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:43:46 +0200 Subject: [PATCH 42/94] remove unused imports --- lib/src/buttons/push_button.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index f25f312f..ad2fc9dc 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -1,8 +1,5 @@ // ignore_for_file: prefer_if_null_operators -import 'dart:async'; - -import 'package:appkit_ui_element_colors/appkit_ui_element_colors.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:gradient_borders/gradient_borders.dart'; From 04f2a11838fafb65065d85347bdd5e9eced38080 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:44:46 +0200 Subject: [PATCH 43/94] remove reference to deprecated API in documentation --- lib/src/buttons/push_button.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index ad2fc9dc..6752957e 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -197,8 +197,7 @@ class PushButton extends StatefulWidget { /// Whether the button is used as a secondary action button (e.g. Cancel buttons in dialogs) /// - /// Sets its background color to [PushButtonThemeData]'s [secondaryColor] attributes (defaults - /// are gray colors). Can still be overridden if the [color] attribute is non-null. + /// Can still be overridden if the [color] attribute is non-null. final bool? secondary; /// Whether the button is enabled or disabled. Buttons are disabled by default. To From 7e0f365435811f164b65da962dbde2779a7e58e4 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:46:24 +0200 Subject: [PATCH 44/94] deprecate `pressedOpacity` --- lib/src/buttons/push_button.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index 6752957e..4dfbd92e 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -171,6 +171,8 @@ class PushButton extends StatefulWidget { /// /// This defaults to 0.4. If null, opacity will not change on pressed if using /// your own custom effects is desired. + @Deprecated("'PushButton' now attempts to mimic macOS’ look and feel, " + "therefore, its opacity no longer changes when it is pressed.") final double? pressedOpacity; /// The radius of the button's corners when it has a background color. From f27803de53b0d8489c20e3fc59a37160ce282197 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:47:58 +0200 Subject: [PATCH 45/94] remove deprecated `pressedOpacity` from `debugFillProperties` --- lib/src/buttons/push_button.dart | 1 - test/buttons/push_button_test.dart | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index 4dfbd92e..a295498b 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -212,7 +212,6 @@ class PushButton extends StatefulWidget { properties.add(EnumProperty('controlSize', controlSize)); properties.add(ColorProperty('color', color)); properties.add(ColorProperty('disabledColor', disabledColor)); - properties.add(DoubleProperty('pressedOpacity', pressedOpacity)); properties.add(DiagnosticsProperty('alignment', alignment)); properties.add(StringProperty('semanticLabel', semanticLabel)); properties.add(DiagnosticsProperty('borderRadius', borderRadius)); diff --git a/test/buttons/push_button_test.dart b/test/buttons/push_button_test.dart index 8bb294a0..981c21df 100644 --- a/test/buttons/push_button_test.dart +++ b/test/buttons/push_button_test.dart @@ -99,7 +99,6 @@ void main() { 'controlSize: regular', 'color: null', 'disabledColor: null', - 'pressedOpacity: 0.4', 'alignment: Alignment.center', 'semanticLabel: null', 'borderRadius: BorderRadius.circular(4.0)', From cfacc99cd6905106fcee3d7fe15f32060a5ae822 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:50:27 +0200 Subject: [PATCH 46/94] document `AccentColor` --- lib/src/enums/accent_color.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/enums/accent_color.dart b/lib/src/enums/accent_color.dart index ed21cfe8..2090e6f4 100644 --- a/lib/src/enums/accent_color.dart +++ b/lib/src/enums/accent_color.dart @@ -1,4 +1,5 @@ -// TODO: document this +/// The macOS accent color which can be changed by the user in *System Settings* +/// → *Apperance* → *Accent color*. enum AccentColor { blue, purple, From 5cf616f430d005c7139a38f24c66e540ab38164a Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 14:51:32 +0200 Subject: [PATCH 47/94] document `AccentColor` values --- lib/src/enums/accent_color.dart | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/src/enums/accent_color.dart b/lib/src/enums/accent_color.dart index 2090e6f4..3e5ecc8e 100644 --- a/lib/src/enums/accent_color.dart +++ b/lib/src/enums/accent_color.dart @@ -1,12 +1,27 @@ /// The macOS accent color which can be changed by the user in *System Settings* /// → *Apperance* → *Accent color*. enum AccentColor { + /// The blue accent color. blue, + + /// The purple accent color. purple, + + /// The pink accent color. pink, + + /// The red accent color. red, + + /// The orange accent color. orange, + + /// The yellow accent color. yellow, + + /// The green accent color. green, + + /// The graphite accent color. graphite, } From 23ab438e81cf827ddf369a97a126f0d23920066e Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 15:36:02 +0200 Subject: [PATCH 48/94] increment version number --- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index f642de3a..49e48c7f 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -174,7 +174,7 @@ packages: path: ".." relative: true source: path - version: "2.0.0" + version: "2.0.1" macos_window_utils: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 64a10b1e..d0525137 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 +version: 2.0.1 homepage: "https://macosui.dev" repository: "https://github.com/GroovinChip/macos_ui" From 4aa22965ad0f4855ec310b7c186b75a335b0ff39 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Mon, 7 Aug 2023 15:36:16 +0200 Subject: [PATCH 49/94] add changelog entry for version 2.0.1 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c59bae9..e1c43f00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [2.0.1] +* `PushButton` has received a facelift. It now mimics the look of native macOS buttons more closely. + * **Note:** As a result, its `pressedOpacity` property and the `PushButtonTheme` class have been deprecated. + ## [2.0.0] ### 🚨 Breaking Changes 🚨 * `macos_ui` has been migrated to utilize [macos_window_utils](https://pub.dev/packages/macos_window_utils) under the hood, which provides the following benefits: From c39ba50dfa292d695d28672836d56db93045c738 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Fri, 11 Aug 2023 09:31:40 +0200 Subject: [PATCH 50/94] upgrade to macos_window_utils 1.2.0 --- example/pubspec.lock | 4 ++-- pubspec.lock | 4 ++-- pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 49e48c7f..86bd412e 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -179,10 +179,10 @@ packages: dependency: transitive description: name: macos_window_utils - sha256: b78a210aa70ca7ccad6e7b7b810fb4689c507f4a46e299214900b2a1eb70ea23 + sha256: "43a90473f8786f00f07203e6819dab67e032f8896dafa4a6f85fbc71fba32c0b" url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.2.0" matcher: dependency: transitive description: diff --git a/pubspec.lock b/pubspec.lock index 018f3b58..6aa59489 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -215,10 +215,10 @@ packages: dependency: "direct main" description: name: macos_window_utils - sha256: b78a210aa70ca7ccad6e7b7b810fb4689c507f4a46e299214900b2a1eb70ea23 + sha256: "43a90473f8786f00f07203e6819dab67e032f8896dafa4a6f85fbc71fba32c0b" url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.2.0" matcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d0525137..8ffa130b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ environment: dependencies: flutter: sdk: flutter - macos_window_utils: ^1.1.3 + macos_window_utils: ^1.2.0 gradient_borders: ^1.0.0 appkit_ui_element_colors: ^1.0.0 From 6a5dbe6a8f92d0601a47fb836e34e17dd2f492bb Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Fri, 11 Aug 2023 09:35:29 +0200 Subject: [PATCH 51/94] initialize `_isWindowMain` properly --- lib/src/utils.dart | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index cc3f765c..19a23909 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -101,9 +101,7 @@ class WindowMainStateListener { NSWindowDelegateHandle? handle; /// Whether the window is currently the main window. - bool _isWindowMain = - true; // TODO: Initialize properly once macos_window_utils supports that, - // see https://github.com/macosui/macos_window_utils.dart/issues/31. + bool _isWindowMain = true; /// Whether the window is currently the main window. bool get isWindowMain => _isWindowMain; @@ -120,6 +118,12 @@ class WindowMainStateListener { if (kIsWeb) return; if (!Platform.isMacOS) return; + _initDelegate(); + _initIsWindowMain(); + } + + /// Initializes the [NSWindowDelegate] to listen for main window changes. + void _initDelegate() { final delegate = _WindowMainStateListenerDelegate( onWindowDidBecomeMain: () { _isWindowMain = true; @@ -133,6 +137,12 @@ class WindowMainStateListener { handle = WindowManipulator.addNSWindowDelegate(delegate); } + /// Initializes the [_isWindowMain] variable. + Future _initIsWindowMain() async { + _isWindowMain = await WindowManipulator.isMainWindow(); + _windowMainStateStreamController.add(_isWindowMain); + } + /// Deinitializes the listener. void deinit() { handle?.removeFromHandler(); From 6aa9d8ac802fc1dc505c8c2b7e3b0920b07652d3 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Fri, 11 Aug 2023 09:37:18 +0200 Subject: [PATCH 52/94] rename `isWindowMain` to `isMainWindow` --- lib/src/buttons/push_button.dart | 4 ++-- lib/src/utils.dart | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index a295498b..678c8510 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -261,7 +261,7 @@ class PushButtonState extends State BoxDecoration _getBoxDecoration() { // If the window isn’t currently the main window (that is, it is not in // focus), make the button look as if it was a secondary button. - final isWindowMain = WindowMainStateListener.instance.isWindowMain; + final isWindowMain = WindowMainStateListener.instance.isMainWindow; return _BoxDecorationBuilder.buildBoxDecoration( accentColor: _accentColor, @@ -278,7 +278,7 @@ class PushButtonState extends State // If the window isn’t currently the main window (that is, it is not in // focus), make the button look as if it was a secondary button. - final isWindowMain = WindowMainStateListener.instance.isWindowMain; + final isWindowMain = WindowMainStateListener.instance.isMainWindow; return MacosDynamicColor.resolve( widget.color ?? diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 19a23909..3d9a8693 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -101,10 +101,10 @@ class WindowMainStateListener { NSWindowDelegateHandle? handle; /// Whether the window is currently the main window. - bool _isWindowMain = true; + bool _isMainWindow = true; /// Whether the window is currently the main window. - bool get isWindowMain => _isWindowMain; + bool get isMainWindow => _isMainWindow; /// Notifies listeners when the window’s main state changes. final _windowMainStateStreamController = StreamController.broadcast(); @@ -126,21 +126,21 @@ class WindowMainStateListener { void _initDelegate() { final delegate = _WindowMainStateListenerDelegate( onWindowDidBecomeMain: () { - _isWindowMain = true; + _isMainWindow = true; _windowMainStateStreamController.add(true); }, onWindowDidResignMain: () { - _isWindowMain = false; + _isMainWindow = false; _windowMainStateStreamController.add(false); }, ); handle = WindowManipulator.addNSWindowDelegate(delegate); } - /// Initializes the [_isWindowMain] variable. + /// Initializes the [_isMainWindow] variable. Future _initIsWindowMain() async { - _isWindowMain = await WindowManipulator.isMainWindow(); - _windowMainStateStreamController.add(_isWindowMain); + _isMainWindow = await WindowManipulator.isMainWindow(); + _windowMainStateStreamController.add(_isMainWindow); } /// Deinitializes the listener. From db655a7c8e774d8d5397b2c89685493681595e7f Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Fri, 11 Aug 2023 09:39:56 +0200 Subject: [PATCH 53/94] replace `print` with `debugPrint` --- lib/src/utils.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 3d9a8693..470b803f 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -267,8 +267,7 @@ class AccentColorListener { return hueComponentToAccentColor[hueComponent]!; } - // ignore: avoid_print - print( + debugPrint( 'Warning: Falling back on slow accent color resolution. It’s possible ' 'that the accent colors have changed in a recent version of macOS, thus ' 'invalidating macos_ui’s accent colors, which were captured in macOS ' From 2058812c5ec09f09b713e8de8a9cbcc1288db89c Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Fri, 11 Aug 2023 09:50:42 +0200 Subject: [PATCH 54/94] deprecate properties of `PushButtonThemeData` rather than `PushButtonThemeData` itself to avoid breaking changes --- lib/src/theme/macos_theme.dart | 7 ++--- lib/src/theme/push_button_theme.dart | 45 ++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/lib/src/theme/macos_theme.dart b/lib/src/theme/macos_theme.dart index 87405882..0bda4d11 100644 --- a/lib/src/theme/macos_theme.dart +++ b/lib/src/theme/macos_theme.dart @@ -514,8 +514,7 @@ class MacosThemeData with Diagnosticable { typography: MacosTypography.lerp(a.typography, b.typography, t), helpButtonTheme: HelpButtonThemeData.lerp(a.helpButtonTheme, b.helpButtonTheme, t), - pushButtonTheme: - PushButtonThemeData.lerp(a.pushButtonTheme, b.pushButtonTheme, t), + pushButtonTheme: a.pushButtonTheme, tooltipTheme: MacosTooltipThemeData.lerp(a.tooltipTheme, b.tooltipTheme, t), visualDensity: VisualDensity.lerp(a.visualDensity, b.visualDensity, t), @@ -581,7 +580,7 @@ class MacosThemeData with Diagnosticable { canvasColor: canvasColor ?? this.canvasColor, dividerColor: dividerColor ?? this.dividerColor, typography: this.typography.merge(typography), - pushButtonTheme: this.pushButtonTheme.merge(pushButtonTheme), + pushButtonTheme: this.pushButtonTheme, helpButtonTheme: this.helpButtonTheme.merge(helpButtonTheme), tooltipTheme: this.tooltipTheme.merge(tooltipTheme), visualDensity: visualDensity ?? this.visualDensity, @@ -605,7 +604,7 @@ class MacosThemeData with Diagnosticable { canvasColor: other.canvasColor, dividerColor: other.dividerColor, typography: typography.merge(other.typography), - pushButtonTheme: pushButtonTheme.merge(other.pushButtonTheme), + pushButtonTheme: pushButtonTheme, helpButtonTheme: helpButtonTheme.merge(other.helpButtonTheme), tooltipTheme: tooltipTheme.merge(other.tooltipTheme), visualDensity: other.visualDensity, diff --git a/lib/src/theme/push_button_theme.dart b/lib/src/theme/push_button_theme.dart index dc35c2b7..0c69aebd 100644 --- a/lib/src/theme/push_button_theme.dart +++ b/lib/src/theme/push_button_theme.dart @@ -7,12 +7,8 @@ import 'package:macos_ui/src/library.dart'; /// See also: /// /// * [PushButtonThemeData], which is used to configure this theme. -@Deprecated("'PushButton' no longer uses singular colors and therefore cannot " - "be themed using a 'PushButtonTheme'.") class PushButtonTheme extends InheritedTheme { /// Create a [PushButtonTheme]. - /// - /// The [data] parameter must not be null. const PushButtonTheme({ super.key, required this.data, @@ -20,6 +16,9 @@ class PushButtonTheme extends InheritedTheme { }); /// The configuration of this theme. + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") final PushButtonThemeData data; /// The closest instance of this class that encloses the given context. @@ -32,6 +31,9 @@ class PushButtonTheme extends InheritedTheme { /// ```dart /// PushButtonTheme theme = PushButtonTheme.of(context); /// ``` + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") static PushButtonThemeData of(BuildContext context) { final PushButtonTheme? buttonTheme = context.dependOnInheritedWidgetOfExactType(); @@ -39,11 +41,17 @@ class PushButtonTheme extends InheritedTheme { } @override + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") Widget wrap(BuildContext context, Widget child) { return PushButtonTheme(data: data, child: child); } @override + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") bool updateShouldNotify(PushButtonTheme oldWidget) => data != oldWidget.data; } @@ -56,8 +64,6 @@ class PushButtonTheme extends InheritedTheme { /// * [PushButtonTheme], the theme which is configured with this class. /// * [MacosThemeData.pushButtonTheme], which can be used to override the default /// style for [PushButton]s below the overall [MacosTheme]. -@Deprecated("'PushButton' no longer uses singular colors and therefore cannot " - "be themed using a 'PushButtonTheme'.") class PushButtonThemeData with Diagnosticable { /// Creates a [PushButtonThemeData]. const PushButtonThemeData({ @@ -67,15 +73,27 @@ class PushButtonThemeData with Diagnosticable { }); /// The default background color for [PushButton] + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") final Color? color; /// The default disabled color for [PushButton] + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") final Color? disabledColor; /// The default secondary color (e.g. Cancel/Go back buttons) for [PushButton] + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") final Color? secondaryColor; /// Copies this [PushButtonThemeData] into another. + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") PushButtonThemeData copyWith({ Color? color, Color? disabledColor, @@ -91,6 +109,9 @@ class PushButtonThemeData with Diagnosticable { /// Linearly interpolate between two [PushButtonThemeData]. /// /// All the properties must be non-null. + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") static PushButtonThemeData lerp( PushButtonThemeData a, PushButtonThemeData b, @@ -104,6 +125,9 @@ class PushButtonThemeData with Diagnosticable { } @override + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") bool operator ==(Object other) => identical(this, other) || other is PushButtonThemeData && @@ -113,9 +137,15 @@ class PushButtonThemeData with Diagnosticable { secondaryColor?.value == other.secondaryColor?.value; @override + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") int get hashCode => color.hashCode ^ disabledColor.hashCode; @override + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(ColorProperty('color', color)); @@ -124,6 +154,9 @@ class PushButtonThemeData with Diagnosticable { } /// Merges this [PushButtonThemeData] with another. + @Deprecated( + "'PushButton' no longer uses singular colors and therefore cannot " + "be themed using a 'PushButtonTheme'.") PushButtonThemeData merge(PushButtonThemeData? other) { if (other == null) return this; return copyWith( From c017df4f1563fe1ef8c9a641b340fca32b72e50c Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Fri, 11 Aug 2023 09:51:36 +0200 Subject: [PATCH 55/94] fix typo in `AccentColor` documentation --- lib/src/enums/accent_color.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/enums/accent_color.dart b/lib/src/enums/accent_color.dart index 3e5ecc8e..95ab8ea2 100644 --- a/lib/src/enums/accent_color.dart +++ b/lib/src/enums/accent_color.dart @@ -1,5 +1,5 @@ /// The macOS accent color which can be changed by the user in *System Settings* -/// → *Apperance* → *Accent color*. +/// → *Appearance* → *Accent color*. enum AccentColor { /// The blue accent color. blue, From 5757632851d3be8da86ff2a257c5be20a5c583b0 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Fri, 11 Aug 2023 09:52:22 +0200 Subject: [PATCH 56/94] fix typo in `AccentColorListener` docs --- lib/src/utils.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 470b803f..5ef5a178 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -207,7 +207,7 @@ class AccentColorListener { /// changes. Stream get onChangedStream => _accentColorStreamController.stream; - /// A stream subcription for the [SystemColorObserver] stream. + /// A stream subscription for the [SystemColorObserver] stream. StreamSubscription? _systemColorObserverStreamSubscription; /// Initializes this class. From c97b9013f15947f84ab2b6080514cb25a4966fd8 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Fri, 11 Aug 2023 10:10:00 +0200 Subject: [PATCH 57/94] remove push button theme test --- test/theme/push_button_theme_test.dart | 97 -------------------------- 1 file changed, 97 deletions(-) delete mode 100644 test/theme/push_button_theme_test.dart diff --git a/test/theme/push_button_theme_test.dart b/test/theme/push_button_theme_test.dart deleted file mode 100644 index 2ff90243..00000000 --- a/test/theme/push_button_theme_test.dart +++ /dev/null @@ -1,97 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:macos_ui/macos_ui.dart'; -import 'package:macos_ui/src/library.dart'; - -void main() { - group('PushButton theme tests', () { - test('lerps from light to dark', () { - final actual = - PushButtonThemeData.lerp(_pushButtonTheme, _pushButtonThemeDark, 1); - - expect(actual, _pushButtonThemeDark); - }); - - test('lerps from dark to light', () { - final actual = - PushButtonThemeData.lerp(_pushButtonThemeDark, _pushButtonTheme, 1); - - expect(actual, _pushButtonTheme); - }); - - test('copyWith, hashCode, ==', () { - expect( - const PushButtonThemeData(), - const PushButtonThemeData().copyWith(), - ); - expect( - const PushButtonThemeData().hashCode, - const PushButtonThemeData().copyWith().hashCode, - ); - }); - - testWidgets('debugFillProperties', (tester) async { - final builder = DiagnosticPropertiesBuilder(); - PushButtonThemeData( - color: MacosColors.appleBlue, - disabledColor: MacosColors.systemGrayColor.color, - secondaryColor: MacosColors.controlColor.color, - ).debugFillProperties(builder); - - final description = builder.properties - .where((node) => !node.isFiltered(DiagnosticLevel.info)) - .map((node) => node.toString()) - .toList(); - - expect( - description, - [ - 'color: MacosColor(0xff0433ff)', - 'disabledColor: MacosColor(0xff8e8e93)', - 'secondaryColor: Color(0x19000000)', - ], - ); - }); - - testWidgets('Default values in widget tree', (tester) async { - late BuildContext capturedContext; - await tester.pumpWidget( - MacosApp( - home: MacosWindow( - disableWallpaperTinting: true, - child: MacosScaffold( - children: [ - ContentArea( - builder: (context, _) { - capturedContext = context; - return const PushButton( - controlSize: ControlSize.regular, - child: Text('Push me'), - ); - }, - ), - ], - ), - ), - ), - ); - - final theme = PushButtonTheme.of(capturedContext); - expect(theme.color, const Color(0xff007aff)); - expect(theme.disabledColor, const Color.fromRGBO(244, 245, 245, 1.0)); - expect(theme.secondaryColor, MacosColors.white); - }); - }); -} - -final _pushButtonTheme = PushButtonThemeData( - color: MacosColors.appleRed, - disabledColor: MacosColors.systemGrayColor.color, - secondaryColor: MacosColors.controlColor.color, -); - -final _pushButtonThemeDark = PushButtonThemeData( - color: MacosColors.appleBlue, - disabledColor: MacosColors.systemGrayColor.darkColor, - secondaryColor: MacosColors.controlColor.darkColor, -); From ce558758b10098a1022c51a3ed43fa724352f653 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha <86920182+Adrian-Samoticha@users.noreply.github.com> Date: Sat, 12 Aug 2023 20:07:47 +0200 Subject: [PATCH 58/94] Update CHANGELOG.md Change the wording of the changelog entry for version 2.0.1. Co-authored-by: Reuben Turner --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1c43f00..95ac5179 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ ## [2.0.1] -* `PushButton` has received a facelift. It now mimics the look of native macOS buttons more closely. +* `PushButton` has received a facelift. It now mimics the look and feel of native macOS buttons more closely. * **Note:** As a result, its `pressedOpacity` property and the `PushButtonTheme` class have been deprecated. ## [2.0.0] From 3c1d16f701a1a44647a416de31472593b96971d9 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha <86920182+Adrian-Samoticha@users.noreply.github.com> Date: Sat, 12 Aug 2023 20:09:54 +0200 Subject: [PATCH 59/94] change documentation of `WindowMainStateListener` Co-authored-by: Reuben Turner --- lib/src/utils.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 5ef5a178..30e89f30 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -91,8 +91,11 @@ class MacOSBrightnessOverrideHandler { } } -/// A class that listens for changes to the application's window being the main -/// window, and notifies listeners. +/// A class that listens for changes to the application's main window. +/// +/// A common use-case for responding to such changes would be to mute the colors of certain +/// primary UI elements when the window is no longer in focus, which is something native +/// macOS applications do out of the box. class WindowMainStateListener { /// A shared instance of [WindowMainStateListener]. static final instance = WindowMainStateListener(); From ad79ce5ca92a59fc38db263d84fabd21c60b5e89 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha <86920182+Adrian-Samoticha@users.noreply.github.com> Date: Sat, 12 Aug 2023 20:16:30 +0200 Subject: [PATCH 60/94] update documentation Co-authored-by: Reuben Turner --- lib/src/utils.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 30e89f30..876504b5 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -178,7 +178,9 @@ class _WindowMainStateListenerDelegate extends NSWindowDelegate { void windowDidResignMain() => onWindowDidResignMain(); } -/// A class that listens to accent color changes. +/// A class that listens to changes to the user's selected system accent color. +/// +/// Native macOS applications respond to such changes immediately. class AccentColorListener { /// A shared instance of [AccentColorListener]. static final instance = AccentColorListener(); From a90c980f8d9822ebd8a16048ca555d2a2da4c70a Mon Sep 17 00:00:00 2001 From: Adrian Samoticha <86920182+Adrian-Samoticha@users.noreply.github.com> Date: Sat, 12 Aug 2023 20:19:28 +0200 Subject: [PATCH 61/94] update documentation Co-authored-by: Reuben Turner --- lib/src/utils.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 876504b5..1a9ae63a 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -208,8 +208,9 @@ class AccentColorListener { /// Notifies listeners when the accent color changes. final _accentColorStreamController = StreamController.broadcast(); - /// An accent color stream. Emits a new value whenever the accent color - /// changes. + /// Streams the user's system accent color selection. + /// + /// Emits a new value whenever the system accent color selection changes. Stream get onChangedStream => _accentColorStreamController.stream; /// A stream subscription for the [SystemColorObserver] stream. From d229e15a4200094487e71dee2ed7b7dfacd75788 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha <86920182+Adrian-Samoticha@users.noreply.github.com> Date: Sat, 12 Aug 2023 20:20:02 +0200 Subject: [PATCH 62/94] update documentation Co-authored-by: Reuben Turner --- lib/src/utils.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 1a9ae63a..1d84d1b0 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -284,7 +284,7 @@ class AccentColorListener { return _slowlyResolveAccentColorFromHueComponent(hueComponent); } - /// This is a fallback method in case the above method fails. + /// This is a fallback method in case [_resolveAccentColorFromHueComponent] fails. AccentColor _slowlyResolveAccentColorFromHueComponent(double hueComponent) { final entries = hueComponentToAccentColor.entries; var lowestDistance = double.maxFinite; From 7d2ec54e99bd76dbba3b2950ff3ce99d7779106e Mon Sep 17 00:00:00 2001 From: Adrian Samoticha <86920182+Adrian-Samoticha@users.noreply.github.com> Date: Sat, 12 Aug 2023 20:29:25 +0200 Subject: [PATCH 63/94] change `var` to `final` Co-authored-by: Reuben Turner --- lib/src/utils.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 1d84d1b0..ac935d5f 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -290,7 +290,7 @@ class AccentColorListener { var lowestDistance = double.maxFinite; var toBeReturnedAccentColor = AccentColor.values.first; - for (var entry in entries) { + for (final entry in entries) { final distance = _distanceBetweenHueComponents(hueComponent, entry.key); if (distance < lowestDistance) { lowestDistance = distance; From a3183b55e99564f00eb900dd530d034427e9c67b Mon Sep 17 00:00:00 2001 From: Adrian Samoticha <86920182+Adrian-Samoticha@users.noreply.github.com> Date: Sat, 12 Aug 2023 20:30:00 +0200 Subject: [PATCH 64/94] update deprecation message Co-authored-by: Reuben Turner --- lib/src/buttons/push_button.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index 678c8510..c49ca98f 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -171,8 +171,8 @@ class PushButton extends StatefulWidget { /// /// This defaults to 0.4. If null, opacity will not change on pressed if using /// your own custom effects is desired. - @Deprecated("'PushButton' now attempts to mimic macOS’ look and feel, " - "therefore, its opacity no longer changes when it is pressed.") + @Deprecated("'PushButton' animations now match their native macOS’ counterparts. " + "Therefore, its opacity no longer changes when it is pressed.") final double? pressedOpacity; /// The radius of the button's corners when it has a background color. From 92658d966486d750969ab70d56c212f4e87a17ee Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sat, 12 Aug 2023 20:41:27 +0200 Subject: [PATCH 65/94] improve formatting --- lib/src/utils.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index ac935d5f..b67fec3d 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -91,7 +91,7 @@ class MacOSBrightnessOverrideHandler { } } -/// A class that listens for changes to the application's main window. +/// A class that listens for changes to the application's main window. /// /// A common use-case for responding to such changes would be to mute the colors of certain /// primary UI elements when the window is no longer in focus, which is something native @@ -208,7 +208,7 @@ class AccentColorListener { /// Notifies listeners when the accent color changes. final _accentColorStreamController = StreamController.broadcast(); - /// Streams the user's system accent color selection. + /// Streams the user's system accent color selection. /// /// Emits a new value whenever the system accent color selection changes. Stream get onChangedStream => _accentColorStreamController.stream; @@ -284,7 +284,8 @@ class AccentColorListener { return _slowlyResolveAccentColorFromHueComponent(hueComponent); } - /// This is a fallback method in case [_resolveAccentColorFromHueComponent] fails. + /// This is a fallback method in case [_resolveAccentColorFromHueComponent] + /// fails. AccentColor _slowlyResolveAccentColorFromHueComponent(double hueComponent) { final entries = hueComponentToAccentColor.entries; var lowestDistance = double.maxFinite; From 7b81123191ec09745d68884b9b3d4cb58ab21cfe Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sat, 12 Aug 2023 20:42:00 +0200 Subject: [PATCH 66/94] improve formatting --- lib/src/utils.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index b67fec3d..18c42a97 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -93,9 +93,9 @@ class MacOSBrightnessOverrideHandler { /// A class that listens for changes to the application's main window. /// -/// A common use-case for responding to such changes would be to mute the colors of certain -/// primary UI elements when the window is no longer in focus, which is something native -/// macOS applications do out of the box. +/// A common use-case for responding to such changes would be to mute the colors +/// of certain primary UI elements when the window is no longer in focus, which +/// is something native macOS applications do out of the box. class WindowMainStateListener { /// A shared instance of [WindowMainStateListener]. static final instance = WindowMainStateListener(); From fb22efa231a91623df2c44d7532d145c86645d87 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sat, 12 Aug 2023 20:47:47 +0200 Subject: [PATCH 67/94] fix grammatical error in debug print message --- lib/src/utils.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 18c42a97..57e980a7 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -276,7 +276,7 @@ class AccentColorListener { debugPrint( 'Warning: Falling back on slow accent color resolution. It’s possible ' 'that the accent colors have changed in a recent version of macOS, thus ' - 'invalidating macos_ui’s accent colors, which were captured in macOS ' + 'invalidating macos_ui’s accent colors, which were captured on macOS ' 'Ventura. If you see this message, please notify a maintainer of the ' 'macos_ui package.', ); From 040b4a0ccb31040ba82eb8b390532d07dcce32fe Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sun, 13 Aug 2023 10:39:32 +0200 Subject: [PATCH 68/94] improve formatting --- lib/src/buttons/push_button.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index c49ca98f..632ea65c 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -171,8 +171,9 @@ class PushButton extends StatefulWidget { /// /// This defaults to 0.4. If null, opacity will not change on pressed if using /// your own custom effects is desired. - @Deprecated("'PushButton' animations now match their native macOS’ counterparts. " - "Therefore, its opacity no longer changes when it is pressed.") + @Deprecated("'PushButton' animations now match their native macOS’ " + "counterparts. Therefore, its opacity no longer changes when it is " + "pressed.") final double? pressedOpacity; /// The radius of the button's corners when it has a background color. From 3a8aecb05a8901d8719f35484101b98659394124 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Tue, 15 Aug 2023 18:58:21 +0200 Subject: [PATCH 69/94] rename `deinit` methods to `dispose` --- lib/src/utils.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 57e980a7..e90be74d 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -100,7 +100,7 @@ class WindowMainStateListener { /// A shared instance of [WindowMainStateListener]. static final instance = WindowMainStateListener(); - /// A [NSWindowDelegateHandle], to be used when deiniting the listener. + /// A [NSWindowDelegateHandle], to be used when disposing the listener. NSWindowDelegateHandle? handle; /// Whether the window is currently the main window. @@ -146,8 +146,8 @@ class WindowMainStateListener { _windowMainStateStreamController.add(_isMainWindow); } - /// Deinitializes the listener. - void deinit() { + /// Disposes this listener. + void dispose() { handle?.removeFromHandler(); } @@ -225,8 +225,8 @@ class AccentColorListener { _initSystemColorObserver(); } - /// Deinitializes this class. - void deinit() { + /// Disposes this listener. + void dispose() { _systemColorObserverStreamSubscription?.cancel(); } From 48c2c26a2a6774b34fcf55ad770decebeaf01be6 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Tue, 15 Aug 2023 19:00:25 +0200 Subject: [PATCH 70/94] move `utils.dart` into `utils` directory --- lib/src/library.dart | 2 +- lib/src/{ => utils}/utils.dart | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename lib/src/{ => utils}/utils.dart (100%) diff --git a/lib/src/library.dart b/lib/src/library.dart index 37fb1228..2273d447 100644 --- a/lib/src/library.dart +++ b/lib/src/library.dart @@ -32,4 +32,4 @@ export 'package:flutter/material.dart' MaterialState; export 'package:flutter/widgets.dart'; -export 'utils.dart'; +export 'utils/utils.dart'; diff --git a/lib/src/utils.dart b/lib/src/utils/utils.dart similarity index 100% rename from lib/src/utils.dart rename to lib/src/utils/utils.dart From ce94aa915ebf6289ed228f312f751674699e4ee3 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Tue, 15 Aug 2023 19:03:08 +0200 Subject: [PATCH 71/94] move `WindowMainStateListener` to separate file --- lib/src/utils/utils.dart | 89 +----------------- lib/src/utils/window_main_state_listener.dart | 92 +++++++++++++++++++ 2 files changed, 94 insertions(+), 87 deletions(-) create mode 100644 lib/src/utils/window_main_state_listener.dart diff --git a/lib/src/utils/utils.dart b/lib/src/utils/utils.dart index e90be74d..0bfd8c2d 100644 --- a/lib/src/utils/utils.dart +++ b/lib/src/utils/utils.dart @@ -8,6 +8,8 @@ import 'package:macos_ui/macos_ui.dart'; import 'package:macos_ui/src/enums/accent_color.dart'; import 'package:macos_ui/src/library.dart'; +export 'window_main_state_listener.dart'; + /// Asserts that the given context has a [MacosTheme] ancestor. /// /// To call this function, use the following pattern, typically in the @@ -91,93 +93,6 @@ class MacOSBrightnessOverrideHandler { } } -/// A class that listens for changes to the application's main window. -/// -/// A common use-case for responding to such changes would be to mute the colors -/// of certain primary UI elements when the window is no longer in focus, which -/// is something native macOS applications do out of the box. -class WindowMainStateListener { - /// A shared instance of [WindowMainStateListener]. - static final instance = WindowMainStateListener(); - - /// A [NSWindowDelegateHandle], to be used when disposing the listener. - NSWindowDelegateHandle? handle; - - /// Whether the window is currently the main window. - bool _isMainWindow = true; - - /// Whether the window is currently the main window. - bool get isMainWindow => _isMainWindow; - - /// Notifies listeners when the window’s main state changes. - final _windowMainStateStreamController = StreamController.broadcast(); - - /// A stream of the window’s main state. Emits a new value whenever the state - /// changes. - Stream get onChangedStream => _windowMainStateStreamController.stream; - - /// Initializes the listener. This should only be called once. - void _init() { - if (kIsWeb) return; - if (!Platform.isMacOS) return; - - _initDelegate(); - _initIsWindowMain(); - } - - /// Initializes the [NSWindowDelegate] to listen for main window changes. - void _initDelegate() { - final delegate = _WindowMainStateListenerDelegate( - onWindowDidBecomeMain: () { - _isMainWindow = true; - _windowMainStateStreamController.add(true); - }, - onWindowDidResignMain: () { - _isMainWindow = false; - _windowMainStateStreamController.add(false); - }, - ); - handle = WindowManipulator.addNSWindowDelegate(delegate); - } - - /// Initializes the [_isMainWindow] variable. - Future _initIsWindowMain() async { - _isMainWindow = await WindowManipulator.isMainWindow(); - _windowMainStateStreamController.add(_isMainWindow); - } - - /// Disposes this listener. - void dispose() { - handle?.removeFromHandler(); - } - - /// A class that listens for changes to the application's window being the - /// main window, and notifies listeners. - WindowMainStateListener() { - _init(); - } -} - -/// The [NSWindowDelegate] used by [WindowMainStateListener]. -class _WindowMainStateListenerDelegate extends NSWindowDelegate { - _WindowMainStateListenerDelegate({ - required this.onWindowDidBecomeMain, - required this.onWindowDidResignMain, - }); - - /// Called when the window becomes the main window. - final void Function() onWindowDidBecomeMain; - - /// Called when the window resigns as the main window. - final void Function() onWindowDidResignMain; - - @override - void windowDidBecomeMain() => onWindowDidBecomeMain(); - - @override - void windowDidResignMain() => onWindowDidResignMain(); -} - /// A class that listens to changes to the user's selected system accent color. /// /// Native macOS applications respond to such changes immediately. diff --git a/lib/src/utils/window_main_state_listener.dart b/lib/src/utils/window_main_state_listener.dart new file mode 100644 index 00000000..932dcb3d --- /dev/null +++ b/lib/src/utils/window_main_state_listener.dart @@ -0,0 +1,92 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:macos_ui/macos_ui.dart'; + +/// A class that listens for changes to the application’s main window. +/// +/// A common use-case for responding to such changes would be to mute the colors +/// of certain primary UI elements when the window is no longer in focus, which +/// is something native macOS applications do out of the box. +class WindowMainStateListener { + /// A shared instance of [WindowMainStateListener]. + static final instance = WindowMainStateListener(); + + /// A [NSWindowDelegateHandle], to be used when disposing the listener. + NSWindowDelegateHandle? handle; + + /// Whether the window is currently the main window. + bool _isMainWindow = true; + + /// Whether the window is currently the main window. + bool get isMainWindow => _isMainWindow; + + /// Notifies listeners when the window’s main state changes. + final _windowMainStateStreamController = StreamController.broadcast(); + + /// A stream of the window’s main state. Emits a new value whenever the state + /// changes. + Stream get onChangedStream => _windowMainStateStreamController.stream; + + /// Initializes the listener. This should only be called once. + void _init() { + if (kIsWeb) return; + if (!Platform.isMacOS) return; + + _initDelegate(); + _initIsWindowMain(); + } + + /// Initializes the [NSWindowDelegate] to listen for main window changes. + void _initDelegate() { + final delegate = _WindowMainStateListenerDelegate( + onWindowDidBecomeMain: () { + _isMainWindow = true; + _windowMainStateStreamController.add(true); + }, + onWindowDidResignMain: () { + _isMainWindow = false; + _windowMainStateStreamController.add(false); + }, + ); + handle = WindowManipulator.addNSWindowDelegate(delegate); + } + + /// Initializes the [_isMainWindow] variable. + Future _initIsWindowMain() async { + _isMainWindow = await WindowManipulator.isMainWindow(); + _windowMainStateStreamController.add(_isMainWindow); + } + + /// Disposes this listener. + void dispose() { + handle?.removeFromHandler(); + } + + /// A class that listens for changes to the application’s window being the + /// main window, and notifies listeners. + WindowMainStateListener() { + _init(); + } +} + +/// The [NSWindowDelegate] used by [WindowMainStateListener]. +class _WindowMainStateListenerDelegate extends NSWindowDelegate { + _WindowMainStateListenerDelegate({ + required this.onWindowDidBecomeMain, + required this.onWindowDidResignMain, + }); + + /// Called when the window becomes the main window. + final void Function() onWindowDidBecomeMain; + + /// Called when the window resigns as the main window. + final void Function() onWindowDidResignMain; + + @override + void windowDidBecomeMain() => onWindowDidBecomeMain(); + + @override + void windowDidResignMain() => onWindowDidResignMain(); +} From ef8c156b62bc18058f8133916206c2db44bef8d9 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Tue, 15 Aug 2023 19:05:10 +0200 Subject: [PATCH 72/94] move `AccentColorListener` to separate file --- lib/src/utils/accent_color_listener.dart | 143 +++++++++++++++++++++++ lib/src/utils/utils.dart | 141 +--------------------- 2 files changed, 144 insertions(+), 140 deletions(-) create mode 100644 lib/src/utils/accent_color_listener.dart diff --git a/lib/src/utils/accent_color_listener.dart b/lib/src/utils/accent_color_listener.dart new file mode 100644 index 00000000..4ba4a491 --- /dev/null +++ b/lib/src/utils/accent_color_listener.dart @@ -0,0 +1,143 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:math'; + +import 'package:appkit_ui_element_colors/appkit_ui_element_colors.dart'; +import 'package:flutter/foundation.dart'; +import 'package:macos_ui/src/enums/accent_color.dart'; + +/// A class that listens to changes to the user’s selected system accent color. +/// +/// Native macOS applications respond to such changes immediately. +class AccentColorListener { + /// A shared instance of [AccentColorListener]. + static final instance = AccentColorListener(); + + /// A map which maps hue components of the [UiElementColor.controlAccentColor] + /// color captured with the [NSAppearanceName.aqua] appearance in the + /// [NSColorSpace.genericRGB] color space to the corresponding [AccentColor]. + static final hueComponentToAccentColor = { + 0.6085324903200698: AccentColor.blue, + 0.8285987697113538: AccentColor.purple, + 0.9209523937489168: AccentColor.pink, + 0.9861913496946438: AccentColor.red, + 0.06543037411201169: AccentColor.orange, + 0.11813830353929083: AccentColor.yellow, + 0.29428158007138466: AccentColor.green, + 0.0: AccentColor.graphite, + }; + + /// The currently active accent color. + AccentColor? _currentAccentColor; + + /// The currently active accent color. + AccentColor? get currentAccentColor => _currentAccentColor; + + /// Notifies listeners when the accent color changes. + final _accentColorStreamController = StreamController.broadcast(); + + /// Streams the user’s system accent color selection. + /// + /// Emits a new value whenever the system accent color selection changes. + Stream get onChangedStream => _accentColorStreamController.stream; + + /// A stream subscription for the [SystemColorObserver] stream. + StreamSubscription? _systemColorObserverStreamSubscription; + + /// Initializes this class. + void _init() { + if (kIsWeb) return; + if (!Platform.isMacOS) return; + + _initCurrentAccentColor(); + _initSystemColorObserver(); + } + + /// Disposes this listener. + void dispose() { + _systemColorObserverStreamSubscription?.cancel(); + } + + /// Initializes the current accent color. This method is to be called whenever + /// a change is detected. + Future _initCurrentAccentColor() async { + final hueComponent = await _getHueComponent(); + _currentAccentColor = _resolveAccentColorFromHueComponent(hueComponent); + _accentColorStreamController.add(null); + } + + /// Initializes the current system color observer. This method may only be + /// called once. + void _initSystemColorObserver() { + assert(_systemColorObserverStreamSubscription == null); + + _systemColorObserverStreamSubscription = + AppkitUiElementColors.systemColorObserver.stream.listen((_) { + _initCurrentAccentColor(); + _accentColorStreamController.add(null); + }); + } + + /// Returns the hue component of the currently active accent color on macOS. + Future _getHueComponent() async { + final color = await AppkitUiElementColors.getColorComponents( + uiElementColor: UiElementColor.controlAccentColor, + components: const { + NSColorComponent.hueComponent, + }, + colorSpace: NSColorSpace.genericRGB, + appearance: NSAppearanceName.aqua, + ); + + assert(color.containsKey("hueComponent")); + + return color["hueComponent"]!; + } + + /// Returns the [AccentColor] which corresponds to the provided + /// [hueComponent]. + AccentColor _resolveAccentColorFromHueComponent(double hueComponent) { + if (hueComponentToAccentColor.containsKey(hueComponent)) { + return hueComponentToAccentColor[hueComponent]!; + } + + debugPrint( + 'Warning: Falling back on slow accent color resolution. It’s possible ' + 'that the accent colors have changed in a recent version of macOS, thus ' + 'invalidating macos_ui’s accent colors, which were captured on macOS ' + 'Ventura. If you see this message, please notify a maintainer of the ' + 'macos_ui package.', + ); + + return _slowlyResolveAccentColorFromHueComponent(hueComponent); + } + + /// This is a fallback method in case [_resolveAccentColorFromHueComponent] + /// fails. + AccentColor _slowlyResolveAccentColorFromHueComponent(double hueComponent) { + final entries = hueComponentToAccentColor.entries; + var lowestDistance = double.maxFinite; + var toBeReturnedAccentColor = AccentColor.values.first; + + for (final entry in entries) { + final distance = _distanceBetweenHueComponents(hueComponent, entry.key); + if (distance < lowestDistance) { + lowestDistance = distance; + toBeReturnedAccentColor = entry.value; + } + } + + return toBeReturnedAccentColor; + } + + /// Returns the distance between two hue components. + double _distanceBetweenHueComponents(double component1, double component2) { + final rawDifference = (component1 - component2).abs(); + return sin(rawDifference * pi); + } + + /// A class that listens to accent color changes. + AccentColorListener() { + _init(); + } +} diff --git a/lib/src/utils/utils.dart b/lib/src/utils/utils.dart index 0bfd8c2d..52930e52 100644 --- a/lib/src/utils/utils.dart +++ b/lib/src/utils/utils.dart @@ -1,14 +1,11 @@ -import 'dart:async'; import 'dart:io'; -import 'dart:math'; -import 'package:appkit_ui_element_colors/appkit_ui_element_colors.dart'; import 'package:flutter/foundation.dart'; import 'package:macos_ui/macos_ui.dart'; -import 'package:macos_ui/src/enums/accent_color.dart'; import 'package:macos_ui/src/library.dart'; export 'window_main_state_listener.dart'; +export 'accent_color_listener.dart'; /// Asserts that the given context has a [MacosTheme] ancestor. /// @@ -92,139 +89,3 @@ class MacOSBrightnessOverrideHandler { _lastBrightness = currentBrightness; } } - -/// A class that listens to changes to the user's selected system accent color. -/// -/// Native macOS applications respond to such changes immediately. -class AccentColorListener { - /// A shared instance of [AccentColorListener]. - static final instance = AccentColorListener(); - - /// A map which maps hue components of the [UiElementColor.controlAccentColor] - /// color captured with the [NSAppearanceName.aqua] appearance in the - /// [NSColorSpace.genericRGB] color space to the corresponding [AccentColor]. - static final hueComponentToAccentColor = { - 0.6085324903200698: AccentColor.blue, - 0.8285987697113538: AccentColor.purple, - 0.9209523937489168: AccentColor.pink, - 0.9861913496946438: AccentColor.red, - 0.06543037411201169: AccentColor.orange, - 0.11813830353929083: AccentColor.yellow, - 0.29428158007138466: AccentColor.green, - 0.0: AccentColor.graphite, - }; - - /// The currently active accent color. - AccentColor? _currentAccentColor; - - /// The currently active accent color. - AccentColor? get currentAccentColor => _currentAccentColor; - - /// Notifies listeners when the accent color changes. - final _accentColorStreamController = StreamController.broadcast(); - - /// Streams the user's system accent color selection. - /// - /// Emits a new value whenever the system accent color selection changes. - Stream get onChangedStream => _accentColorStreamController.stream; - - /// A stream subscription for the [SystemColorObserver] stream. - StreamSubscription? _systemColorObserverStreamSubscription; - - /// Initializes this class. - void _init() { - if (kIsWeb) return; - if (!Platform.isMacOS) return; - - _initCurrentAccentColor(); - _initSystemColorObserver(); - } - - /// Disposes this listener. - void dispose() { - _systemColorObserverStreamSubscription?.cancel(); - } - - /// Initializes the current accent color. This method is to be called whenever - /// a change is detected. - Future _initCurrentAccentColor() async { - final hueComponent = await _getHueComponent(); - _currentAccentColor = _resolveAccentColorFromHueComponent(hueComponent); - _accentColorStreamController.add(null); - } - - /// Initializes the current system color observer. This method may only be - /// called once. - void _initSystemColorObserver() { - assert(_systemColorObserverStreamSubscription == null); - - _systemColorObserverStreamSubscription = - AppkitUiElementColors.systemColorObserver.stream.listen((_) { - _initCurrentAccentColor(); - _accentColorStreamController.add(null); - }); - } - - /// Returns the hue component of the currently active accent color on macOS. - Future _getHueComponent() async { - final color = await AppkitUiElementColors.getColorComponents( - uiElementColor: UiElementColor.controlAccentColor, - components: const { - NSColorComponent.hueComponent, - }, - colorSpace: NSColorSpace.genericRGB, - appearance: NSAppearanceName.aqua, - ); - - assert(color.containsKey("hueComponent")); - - return color["hueComponent"]!; - } - - /// Returns the [AccentColor] which corresponds to the provided - /// [hueComponent]. - AccentColor _resolveAccentColorFromHueComponent(double hueComponent) { - if (hueComponentToAccentColor.containsKey(hueComponent)) { - return hueComponentToAccentColor[hueComponent]!; - } - - debugPrint( - 'Warning: Falling back on slow accent color resolution. It’s possible ' - 'that the accent colors have changed in a recent version of macOS, thus ' - 'invalidating macos_ui’s accent colors, which were captured on macOS ' - 'Ventura. If you see this message, please notify a maintainer of the ' - 'macos_ui package.', - ); - - return _slowlyResolveAccentColorFromHueComponent(hueComponent); - } - - /// This is a fallback method in case [_resolveAccentColorFromHueComponent] - /// fails. - AccentColor _slowlyResolveAccentColorFromHueComponent(double hueComponent) { - final entries = hueComponentToAccentColor.entries; - var lowestDistance = double.maxFinite; - var toBeReturnedAccentColor = AccentColor.values.first; - - for (final entry in entries) { - final distance = _distanceBetweenHueComponents(hueComponent, entry.key); - if (distance < lowestDistance) { - lowestDistance = distance; - toBeReturnedAccentColor = entry.value; - } - } - - return toBeReturnedAccentColor; - } - - /// Returns the distance between two hue components. - double _distanceBetweenHueComponents(double component1, double component2) { - final rawDifference = (component1 - component2).abs(); - return sin(rawDifference * pi); - } - - /// A class that listens to accent color changes. - AccentColorListener() { - _init(); - } -} From 7fa0f23692042bcbcefb30c46b8020a18810c02e Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Tue, 15 Aug 2023 19:08:11 +0200 Subject: [PATCH 73/94] move `MacOSBrightnessOverrideHandler` to separate file --- .../macos_brightness_override_handler.dart | 25 +++++++++++++++++++ lib/src/utils/utils.dart | 25 +------------------ 2 files changed, 26 insertions(+), 24 deletions(-) create mode 100644 lib/src/utils/macos_brightness_override_handler.dart diff --git a/lib/src/utils/macos_brightness_override_handler.dart b/lib/src/utils/macos_brightness_override_handler.dart new file mode 100644 index 00000000..3fad912b --- /dev/null +++ b/lib/src/utils/macos_brightness_override_handler.dart @@ -0,0 +1,25 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:macos_ui/macos_ui.dart'; + +/// A class that ensures that the application’s macOS window’s brightness +/// matches the given brightness. +class MacOSBrightnessOverrideHandler { + static Brightness? _lastBrightness; + + /// Ensures that the application’s macOS window’s brightness matches + /// [currentBrightness]. + /// + /// For performance reasons, the brightness setting will only be overridden if + /// [currentBrightness] differs from the value it had when this method was + /// previously called. Therefore, it is safe to call this method frequently. + static void ensureMatchingBrightness(Brightness currentBrightness) { + if (kIsWeb) return; + if (!Platform.isMacOS) return; + if (currentBrightness == _lastBrightness) return; + + WindowManipulator.overrideMacOSBrightness(dark: currentBrightness.isDark); + _lastBrightness = currentBrightness; + } +} diff --git a/lib/src/utils/utils.dart b/lib/src/utils/utils.dart index 52930e52..3d63855c 100644 --- a/lib/src/utils/utils.dart +++ b/lib/src/utils/utils.dart @@ -1,11 +1,9 @@ -import 'dart:io'; - -import 'package:flutter/foundation.dart'; import 'package:macos_ui/macos_ui.dart'; import 'package:macos_ui/src/library.dart'; export 'window_main_state_listener.dart'; export 'accent_color_listener.dart'; +export 'macos_brightness_override_handler.dart'; /// Asserts that the given context has a [MacosTheme] ancestor. /// @@ -68,24 +66,3 @@ class Unsupported { final String message; } - -/// A class that ensures that the application's macOS window's brightness -/// matches the given brightness. -class MacOSBrightnessOverrideHandler { - static Brightness? _lastBrightness; - - /// Ensures that the application's macOS window's brightness matches - /// [currentBrightness]. - /// - /// For performance reasons, the brightness setting will only be overridden if - /// [currentBrightness] differs from the value it had when this method was - /// previously called. Therefore, it is safe to call this method frequently. - static void ensureMatchingBrightness(Brightness currentBrightness) { - if (kIsWeb) return; - if (!Platform.isMacOS) return; - if (currentBrightness == _lastBrightness) return; - - WindowManipulator.overrideMacOSBrightness(dark: currentBrightness.isDark); - _lastBrightness = currentBrightness; - } -} From 3e50ae4034afcb76862b7a0e7728900cdec77b75 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Tue, 15 Aug 2023 19:54:08 +0200 Subject: [PATCH 74/94] add example to `AccentColorListener` --- lib/src/utils/accent_color_listener.dart | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/src/utils/accent_color_listener.dart b/lib/src/utils/accent_color_listener.dart index 4ba4a491..4dd85a75 100644 --- a/lib/src/utils/accent_color_listener.dart +++ b/lib/src/utils/accent_color_listener.dart @@ -9,6 +9,23 @@ import 'package:macos_ui/src/enums/accent_color.dart'; /// A class that listens to changes to the user’s selected system accent color. /// /// Native macOS applications respond to such changes immediately. +/// +/// Example using [StreamBuilder]: +/// +/// ```dart +/// StreamBuilder( +/// stream: AccentColorListener.instance.onChangedStream, +/// builder: (context, _) { +/// final AccentColor? accentColor = +/// AccentColorListener.instance.currentAccentColor; +/// +/// return SomeWidget( +/// accentColor: accentColor, +/// child: ... +/// ); +/// }, +/// ); +/// ``` class AccentColorListener { /// A shared instance of [AccentColorListener]. static final instance = AccentColorListener(); From 47e69740a9324da56fa8bd3ba5001c5e29091009 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Tue, 15 Aug 2023 19:56:51 +0200 Subject: [PATCH 75/94] rename `isWindowMain` to `isMainWindow` --- lib/src/buttons/push_button.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index 632ea65c..b94e1822 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -262,13 +262,13 @@ class PushButtonState extends State BoxDecoration _getBoxDecoration() { // If the window isn’t currently the main window (that is, it is not in // focus), make the button look as if it was a secondary button. - final isWindowMain = WindowMainStateListener.instance.isMainWindow; + final isMainWindow = WindowMainStateListener.instance.isMainWindow; return _BoxDecorationBuilder.buildBoxDecoration( accentColor: _accentColor, isEnabled: widget.enabled, isDarkModeEnabled: MacosTheme.of(context).brightness.isDark, - isSecondary: !isWindowMain || (widget.secondary ?? false), + isSecondary: !isMainWindow || (widget.secondary ?? false), ); } From 7b9b1b6fd9ebddfe7e55e63d3ec59683045e9022 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Tue, 15 Aug 2023 19:57:55 +0200 Subject: [PATCH 76/94] add example to `WindowMainStateListener` --- lib/src/utils/window_main_state_listener.dart | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/src/utils/window_main_state_listener.dart b/lib/src/utils/window_main_state_listener.dart index 932dcb3d..43b5eafd 100644 --- a/lib/src/utils/window_main_state_listener.dart +++ b/lib/src/utils/window_main_state_listener.dart @@ -9,6 +9,23 @@ import 'package:macos_ui/macos_ui.dart'; /// A common use-case for responding to such changes would be to mute the colors /// of certain primary UI elements when the window is no longer in focus, which /// is something native macOS applications do out of the box. +/// +/// Example using [StreamBuilder]: +/// +/// ```dart +/// StreamBuilder( +/// stream: WindowMainStateListener.instance.onChangedStream, +/// builder: (context, _) { +/// final bool isMainWindow +/// = WindowMainStateListener.instance.isMainWindow; +/// +/// return SomeWidget( +/// isMainWindow: isMainWindow, +/// child: ... +/// ); +/// }, +/// ); +/// ``` class WindowMainStateListener { /// A shared instance of [WindowMainStateListener]. static final instance = WindowMainStateListener(); From a4b6c45133271512a81f544a5692cb7c1c47c289 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sat, 19 Aug 2023 15:28:45 +0200 Subject: [PATCH 77/94] =?UTF-8?q?replace=20`child:=20...`=20with=20`child:?= =?UTF-8?q?=20=E2=80=A6`=20to=20avoid=20confusing=20the=20dots=20with=20th?= =?UTF-8?q?e=20spread=20operator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/src/utils/accent_color_listener.dart | 2 +- lib/src/utils/window_main_state_listener.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/utils/accent_color_listener.dart b/lib/src/utils/accent_color_listener.dart index 4dd85a75..fe279cd3 100644 --- a/lib/src/utils/accent_color_listener.dart +++ b/lib/src/utils/accent_color_listener.dart @@ -21,7 +21,7 @@ import 'package:macos_ui/src/enums/accent_color.dart'; /// /// return SomeWidget( /// accentColor: accentColor, -/// child: ... +/// child: … /// ); /// }, /// ); diff --git a/lib/src/utils/window_main_state_listener.dart b/lib/src/utils/window_main_state_listener.dart index 43b5eafd..74db47ef 100644 --- a/lib/src/utils/window_main_state_listener.dart +++ b/lib/src/utils/window_main_state_listener.dart @@ -21,7 +21,7 @@ import 'package:macos_ui/macos_ui.dart'; /// /// return SomeWidget( /// isMainWindow: isMainWindow, -/// child: ... +/// child: … /// ); /// }, /// ); From 4bd7d336d85a6cfa41b7ac1b8f3dd4bd4c8a08a8 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha <86920182+Adrian-Samoticha@users.noreply.github.com> Date: Sat, 19 Aug 2023 16:51:50 +0200 Subject: [PATCH 78/94] Update lib/src/utils/accent_color_listener.dart Co-authored-by: Reuben Turner --- lib/src/utils/accent_color_listener.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/utils/accent_color_listener.dart b/lib/src/utils/accent_color_listener.dart index fe279cd3..c1ccab12 100644 --- a/lib/src/utils/accent_color_listener.dart +++ b/lib/src/utils/accent_color_listener.dart @@ -6,7 +6,7 @@ import 'package:appkit_ui_element_colors/appkit_ui_element_colors.dart'; import 'package:flutter/foundation.dart'; import 'package:macos_ui/src/enums/accent_color.dart'; -/// A class that listens to changes to the user’s selected system accent color. +/// A class that listens for changes to the user’s selected system accent color. /// /// Native macOS applications respond to such changes immediately. /// From 21ebe83bf83699af26ca41b12b87f6f9a03012f7 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha <86920182+Adrian-Samoticha@users.noreply.github.com> Date: Sat, 19 Aug 2023 16:57:44 +0200 Subject: [PATCH 79/94] Update lib/src/utils/accent_color_listener.dart Co-authored-by: Reuben Turner --- lib/src/utils/accent_color_listener.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/utils/accent_color_listener.dart b/lib/src/utils/accent_color_listener.dart index c1ccab12..7b80784a 100644 --- a/lib/src/utils/accent_color_listener.dart +++ b/lib/src/utils/accent_color_listener.dart @@ -95,7 +95,7 @@ class AccentColorListener { }); } - /// Returns the hue component of the currently active accent color on macOS. + /// Returns the hue component of the active accent color selection on macOS. Future _getHueComponent() async { final color = await AppkitUiElementColors.getColorComponents( uiElementColor: UiElementColor.controlAccentColor, From 053befb6b527bdcc80fd1d14c3b4236087a1bfa8 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha <86920182+Adrian-Samoticha@users.noreply.github.com> Date: Sat, 19 Aug 2023 16:58:17 +0200 Subject: [PATCH 80/94] Update lib/src/utils/accent_color_listener.dart Co-authored-by: Reuben Turner --- lib/src/utils/accent_color_listener.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/utils/accent_color_listener.dart b/lib/src/utils/accent_color_listener.dart index 7b80784a..08104c68 100644 --- a/lib/src/utils/accent_color_listener.dart +++ b/lib/src/utils/accent_color_listener.dart @@ -44,7 +44,7 @@ class AccentColorListener { 0.0: AccentColor.graphite, }; - /// The currently active accent color. + /// The active accent color selection. AccentColor? _currentAccentColor; /// The currently active accent color. From 9394422547e799b49e4e4f18f12c29bf353e0f80 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sat, 19 Aug 2023 17:16:32 +0200 Subject: [PATCH 81/94] rename `onChangedStream` to `onChanged` --- lib/src/buttons/push_button.dart | 4 ++-- lib/src/utils/accent_color_listener.dart | 4 ++-- lib/src/utils/window_main_state_listener.dart | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/src/buttons/push_button.dart b/lib/src/buttons/push_button.dart index b94e1822..df0cf4fe 100644 --- a/lib/src/buttons/push_button.dart +++ b/lib/src/buttons/push_button.dart @@ -341,10 +341,10 @@ class PushButtonState extends State child: ConstrainedBox( constraints: widget.controlSize.constraints, child: StreamBuilder( - stream: AccentColorListener.instance.onChangedStream, + stream: AccentColorListener.instance.onChanged, builder: (context, _) { return StreamBuilder( - stream: WindowMainStateListener.instance.onChangedStream, + stream: WindowMainStateListener.instance.onChanged, builder: (context, _) { final Color backgroundColor = _getBackgroundColor(); diff --git a/lib/src/utils/accent_color_listener.dart b/lib/src/utils/accent_color_listener.dart index 08104c68..1a21c64b 100644 --- a/lib/src/utils/accent_color_listener.dart +++ b/lib/src/utils/accent_color_listener.dart @@ -14,7 +14,7 @@ import 'package:macos_ui/src/enums/accent_color.dart'; /// /// ```dart /// StreamBuilder( -/// stream: AccentColorListener.instance.onChangedStream, +/// stream: AccentColorListener.instance.onChanged, /// builder: (context, _) { /// final AccentColor? accentColor = /// AccentColorListener.instance.currentAccentColor; @@ -56,7 +56,7 @@ class AccentColorListener { /// Streams the user’s system accent color selection. /// /// Emits a new value whenever the system accent color selection changes. - Stream get onChangedStream => _accentColorStreamController.stream; + Stream get onChanged => _accentColorStreamController.stream; /// A stream subscription for the [SystemColorObserver] stream. StreamSubscription? _systemColorObserverStreamSubscription; diff --git a/lib/src/utils/window_main_state_listener.dart b/lib/src/utils/window_main_state_listener.dart index 74db47ef..ece89646 100644 --- a/lib/src/utils/window_main_state_listener.dart +++ b/lib/src/utils/window_main_state_listener.dart @@ -14,7 +14,7 @@ import 'package:macos_ui/macos_ui.dart'; /// /// ```dart /// StreamBuilder( -/// stream: WindowMainStateListener.instance.onChangedStream, +/// stream: WindowMainStateListener.instance.onChanged, /// builder: (context, _) { /// final bool isMainWindow /// = WindowMainStateListener.instance.isMainWindow; @@ -44,7 +44,7 @@ class WindowMainStateListener { /// A stream of the window’s main state. Emits a new value whenever the state /// changes. - Stream get onChangedStream => _windowMainStateStreamController.stream; + Stream get onChanged => _windowMainStateStreamController.stream; /// Initializes the listener. This should only be called once. void _init() { From 76caa07b0c98bec7a796839935ad4380406ae3fb Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sat, 19 Aug 2023 17:17:43 +0200 Subject: [PATCH 82/94] move `AccentColorListener` constructor to top of class --- lib/src/utils/accent_color_listener.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/src/utils/accent_color_listener.dart b/lib/src/utils/accent_color_listener.dart index 1a21c64b..5965e3ab 100644 --- a/lib/src/utils/accent_color_listener.dart +++ b/lib/src/utils/accent_color_listener.dart @@ -27,6 +27,11 @@ import 'package:macos_ui/src/enums/accent_color.dart'; /// ); /// ``` class AccentColorListener { + /// A class that listens to accent color changes. + AccentColorListener() { + _init(); + } + /// A shared instance of [AccentColorListener]. static final instance = AccentColorListener(); @@ -152,9 +157,4 @@ class AccentColorListener { final rawDifference = (component1 - component2).abs(); return sin(rawDifference * pi); } - - /// A class that listens to accent color changes. - AccentColorListener() { - _init(); - } } From e8190d6c3505c4513c68f4a5dd5777960675d2bb Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sat, 19 Aug 2023 17:18:16 +0200 Subject: [PATCH 83/94] move `WindowMainStateListener` to top of class --- lib/src/utils/window_main_state_listener.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/src/utils/window_main_state_listener.dart b/lib/src/utils/window_main_state_listener.dart index ece89646..c926f2b3 100644 --- a/lib/src/utils/window_main_state_listener.dart +++ b/lib/src/utils/window_main_state_listener.dart @@ -27,6 +27,12 @@ import 'package:macos_ui/macos_ui.dart'; /// ); /// ``` class WindowMainStateListener { + /// A class that listens for changes to the application’s window being the + /// main window, and notifies listeners. + WindowMainStateListener() { + _init(); + } + /// A shared instance of [WindowMainStateListener]. static final instance = WindowMainStateListener(); @@ -80,12 +86,6 @@ class WindowMainStateListener { void dispose() { handle?.removeFromHandler(); } - - /// A class that listens for changes to the application’s window being the - /// main window, and notifies listeners. - WindowMainStateListener() { - _init(); - } } /// The [NSWindowDelegate] used by [WindowMainStateListener]. From 673c7209bd71b9ac035f5b2f24d0c483451e86a8 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sat, 19 Aug 2023 17:19:36 +0200 Subject: [PATCH 84/94] =?UTF-8?q?add=20=E2=80=9CUpdated=E2=80=9D=20heading?= =?UTF-8?q?=20to=20changelog=20entry=20for=20version=202.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95ac5179..f27d69cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ## [2.0.1] +### 🔄 Updated 🔄 * `PushButton` has received a facelift. It now mimics the look and feel of native macOS buttons more closely. * **Note:** As a result, its `pressedOpacity` property and the `PushButtonTheme` class have been deprecated. From e78cd55eeed26a2073dbb1da70eb047cff60ddf8 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sat, 19 Aug 2023 17:29:28 +0200 Subject: [PATCH 85/94] migrate to `ExcludeSemantics` in popup button (`ignoringSemantics` is deprecated) --- example/pubspec.lock | 38 +++++++++++++++---------------- lib/src/buttons/popup_button.dart | 11 +++++---- pubspec.lock | 38 +++++++++++++++++++------------ 3 files changed, 49 insertions(+), 38 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 86bd412e..f61168d0 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.17.2" crypto: dependency: transitive description: @@ -152,14 +152,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" lints: dependency: transitive description: @@ -187,18 +179,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: @@ -312,10 +304,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: @@ -352,10 +344,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.0" typed_data: dependency: transitive description: @@ -436,6 +428,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" win32: dependency: transitive description: @@ -453,5 +453,5 @@ packages: source: hosted version: "1.0.0" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=3.10.0" diff --git a/lib/src/buttons/popup_button.dart b/lib/src/buttons/popup_button.dart index 8158e63c..37c6559b 100644 --- a/lib/src/buttons/popup_button.dart +++ b/lib/src/buttons/popup_button.dart @@ -1194,10 +1194,13 @@ class _MacosPopupButtonState extends State> } hintIndex = items.length; - items.add(IgnorePointer( - ignoringSemantics: false, - child: displayedHint, - )); + items.add( + ExcludeSemantics( + child: IgnorePointer( + child: displayedHint, + ), + ), + ); } // If value is null (then _selectedIndex is null) then we diff --git a/pubspec.lock b/pubspec.lock index 6aa59489..62a3f534 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -69,10 +69,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.17.2" convert: dependency: transitive description: @@ -223,18 +223,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: @@ -364,10 +364,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: @@ -404,26 +404,26 @@ packages: dependency: transitive description: name: test - sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" + sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" url: "https://pub.dev" source: hosted - version: "1.24.1" + version: "1.24.3" test_api: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.0" test_core: dependency: transitive description: name: test_core - sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" + sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.5.3" typed_data: dependency: transitive description: @@ -456,6 +456,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" web_socket_channel: dependency: transitive description: @@ -481,5 +489,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=3.10.0" From 523cb67dc4038219a88f307855b767a4dff0a020 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sat, 30 Sep 2023 19:36:55 +0200 Subject: [PATCH 86/94] update `LastUpgradeVersion` and `LastUpgradeCheck` in example --- example/macos/Runner.xcodeproj/project.pbxproj | 2 +- .../Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj index 2c562a4e..428da703 100644 --- a/example/macos/Runner.xcodeproj/project.pbxproj +++ b/example/macos/Runner.xcodeproj/project.pbxproj @@ -202,7 +202,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index fb7259e1..83d88728 100644 --- a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ Date: Sat, 30 Sep 2023 19:37:01 +0200 Subject: [PATCH 87/94] fix typo --- lib/src/layout/sidebar/sidebar_item.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/layout/sidebar/sidebar_item.dart b/lib/src/layout/sidebar/sidebar_item.dart index b7e2d487..706412d8 100644 --- a/lib/src/layout/sidebar/sidebar_item.dart +++ b/lib/src/layout/sidebar/sidebar_item.dart @@ -43,7 +43,7 @@ class SidebarItem with Diagnosticable { final Color? unselectedColor; /// The [shape] property specifies the outline (border) of the - /// decoration. The shape must not be null. It's used alonside + /// decoration. The shape must not be null. It's used alongside /// [selectedColor]. final ShapeBorder? shape; From 02d504ef9f7cae0615b5f21cb9244012987737b4 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sat, 30 Sep 2023 21:13:33 +0200 Subject: [PATCH 88/94] format `window.dart` --- lib/src/layout/window.dart | 178 ++++++++++++++++++++----------------- 1 file changed, 94 insertions(+), 84 deletions(-) diff --git a/lib/src/layout/window.dart b/lib/src/layout/window.dart index 85c20417..7db89923 100644 --- a/lib/src/layout/window.dart +++ b/lib/src/layout/window.dart @@ -265,95 +265,105 @@ class _MacosWindowState extends State { minHeight: height, maxHeight: height, ).normalize(), - child: kIsWeb ? ColoredBox( - color: theme.canvasColor, - child: Column( - children: [ - // If an app is running on macOS, apply - // sidebar.topOffset as needed in order to avoid the - // traffic lights. Otherwise, position the sidebar - // by the top of the application's bounds based on - // the presence of sidebar.top. - if (!kIsWeb && sidebar.topOffset > 0) ...[ - SizedBox(height: sidebar.topOffset), - ] else if (sidebar.top != null) ...[ - const SizedBox(height: 12), - ] else - const SizedBox.shrink(), - if (_sidebarScrollController.hasClients && - _sidebarScrollController.offset > 0.0) - Divider(thickness: 1, height: 1, color: dividerColor), - if (sidebar.top != null && constraints.maxHeight > 81) - Padding( - padding: - const EdgeInsets.symmetric(horizontal: 8.0), - child: sidebar.top!, - ), - Expanded( - child: MacosScrollbar( - controller: _sidebarScrollController, - child: Padding( - padding: sidebar.padding, - child: sidebar.builder( - context, - _sidebarScrollController, + child: kIsWeb + ? ColoredBox( + color: theme.canvasColor, + child: Column( + children: [ + // If an app is running on macOS, apply + // sidebar.topOffset as needed in order to avoid the + // traffic lights. Otherwise, position the sidebar + // by the top of the application's bounds based on + // the presence of sidebar.top. + if (!kIsWeb && sidebar.topOffset > 0) ...[ + SizedBox(height: sidebar.topOffset), + ] else if (sidebar.top != null) ...[ + const SizedBox(height: 12), + ] else + const SizedBox.shrink(), + if (_sidebarScrollController.hasClients && + _sidebarScrollController.offset > 0.0) + Divider( + thickness: 1, + height: 1, + color: dividerColor), + if (sidebar.top != null && + constraints.maxHeight > 81) + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0), + child: sidebar.top!, + ), + Expanded( + child: MacosScrollbar( + controller: _sidebarScrollController, + child: Padding( + padding: sidebar.padding, + child: sidebar.builder( + context, + _sidebarScrollController, + ), + ), + ), ), - ), - ), - ), - if (sidebar.bottom != null && - constraints.maxHeight > 141) - Padding( - padding: const EdgeInsets.all(16.0), - child: sidebar.bottom!, + if (sidebar.bottom != null && + constraints.maxHeight > 141) + Padding( + padding: const EdgeInsets.all(16.0), + child: sidebar.bottom!, + ), + ], ), - ], - ), - ) : TransparentMacOSSidebar( - state: sidebarState, - child: Column( - children: [ - // If an app is running on macOS, apply - // sidebar.topOffset as needed in order to avoid the - // traffic lights. Otherwise, position the sidebar - // by the top of the application's bounds based on - // the presence of sidebar.top. - if (!kIsWeb && sidebar.topOffset > 0) ...[ - SizedBox(height: sidebar.topOffset), - ] else if (sidebar.top != null) ...[ - const SizedBox(height: 12), - ] else - const SizedBox.shrink(), - if (_sidebarScrollController.hasClients && - _sidebarScrollController.offset > 0.0) - Divider(thickness: 1, height: 1, color: dividerColor), - if (sidebar.top != null && constraints.maxHeight > 81) - Padding( - padding: - const EdgeInsets.symmetric(horizontal: 8.0), - child: sidebar.top!, - ), - Expanded( - child: MacosScrollbar( - controller: _sidebarScrollController, - child: Padding( - padding: sidebar.padding, - child: sidebar.builder( - context, - _sidebarScrollController, + ) + : TransparentMacOSSidebar( + state: sidebarState, + child: Column( + children: [ + // If an app is running on macOS, apply + // sidebar.topOffset as needed in order to avoid the + // traffic lights. Otherwise, position the sidebar + // by the top of the application's bounds based on + // the presence of sidebar.top. + if (!kIsWeb && sidebar.topOffset > 0) ...[ + SizedBox(height: sidebar.topOffset), + ] else if (sidebar.top != null) ...[ + const SizedBox(height: 12), + ] else + const SizedBox.shrink(), + if (_sidebarScrollController.hasClients && + _sidebarScrollController.offset > 0.0) + Divider( + thickness: 1, + height: 1, + color: dividerColor), + if (sidebar.top != null && + constraints.maxHeight > 81) + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0), + child: sidebar.top!, + ), + Expanded( + child: MacosScrollbar( + controller: _sidebarScrollController, + child: Padding( + padding: sidebar.padding, + child: sidebar.builder( + context, + _sidebarScrollController, + ), + ), + ), ), - ), + if (sidebar.bottom != null && + constraints.maxHeight > 141) + Padding( + padding: const EdgeInsets.all(16.0), + child: sidebar.bottom!, + ), + ], ), ), - if (sidebar.bottom != null && - constraints.maxHeight > 141) - Padding( - padding: const EdgeInsets.all(16.0), - child: sidebar.bottom!, - ), - ], - ), - ), ), ), From 6557d05e7672fe02e417900f9d9749ee2f99ccb0 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sat, 30 Sep 2023 21:14:47 +0200 Subject: [PATCH 89/94] improve formatting for `window.dart` --- lib/src/layout/window.dart | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/src/layout/window.dart b/lib/src/layout/window.dart index 7db89923..fa32d640 100644 --- a/lib/src/layout/window.dart +++ b/lib/src/layout/window.dart @@ -271,10 +271,10 @@ class _MacosWindowState extends State { child: Column( children: [ // If an app is running on macOS, apply - // sidebar.topOffset as needed in order to avoid the - // traffic lights. Otherwise, position the sidebar - // by the top of the application's bounds based on - // the presence of sidebar.top. + // sidebar.topOffset as needed in order to avoid + // the traffic lights. Otherwise, position the + // sidebar by the top of the application's bounds + // based on the presence of sidebar.top. if (!kIsWeb && sidebar.topOffset > 0) ...[ SizedBox(height: sidebar.topOffset), ] else if (sidebar.top != null) ...[ @@ -291,7 +291,8 @@ class _MacosWindowState extends State { constraints.maxHeight > 81) Padding( padding: const EdgeInsets.symmetric( - horizontal: 8.0), + horizontal: 8.0, + ), child: sidebar.top!, ), Expanded( @@ -320,10 +321,10 @@ class _MacosWindowState extends State { child: Column( children: [ // If an app is running on macOS, apply - // sidebar.topOffset as needed in order to avoid the - // traffic lights. Otherwise, position the sidebar - // by the top of the application's bounds based on - // the presence of sidebar.top. + // sidebar.topOffset as needed in order to avoid + // the traffic lights. Otherwise, position the + // sidebar by the top of the application's bounds + // based on the presence of sidebar.top. if (!kIsWeb && sidebar.topOffset > 0) ...[ SizedBox(height: sidebar.topOffset), ] else if (sidebar.top != null) ...[ @@ -340,7 +341,8 @@ class _MacosWindowState extends State { constraints.maxHeight > 81) Padding( padding: const EdgeInsets.symmetric( - horizontal: 8.0), + horizontal: 8.0, + ), child: sidebar.top!, ), Expanded( From 97751dc46ac0a4f8e598faf69974c70bf7389ebb Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Tue, 3 Oct 2023 23:56:14 +0200 Subject: [PATCH 90/94] fix sidebar appearing too dark --- lib/src/layout/window.dart | 90 ++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/lib/src/layout/window.dart b/lib/src/layout/window.dart index fa32d640..d28511c0 100644 --- a/lib/src/layout/window.dart +++ b/lib/src/layout/window.dart @@ -318,52 +318,58 @@ class _MacosWindowState extends State { ) : TransparentMacOSSidebar( state: sidebarState, - child: Column( - children: [ - // If an app is running on macOS, apply - // sidebar.topOffset as needed in order to avoid - // the traffic lights. Otherwise, position the - // sidebar by the top of the application's bounds - // based on the presence of sidebar.top. - if (!kIsWeb && sidebar.topOffset > 0) ...[ - SizedBox(height: sidebar.topOffset), - ] else if (sidebar.top != null) ...[ - const SizedBox(height: 12), - ] else - const SizedBox.shrink(), - if (_sidebarScrollController.hasClients && - _sidebarScrollController.offset > 0.0) - Divider( - thickness: 1, - height: 1, - color: dividerColor), - if (sidebar.top != null && - constraints.maxHeight > 81) - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, + child: DecoratedBox( + decoration: const BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 1.0), + backgroundBlendMode: BlendMode.clear, + ), + child: Column( + children: [ + // If an app is running on macOS, apply + // sidebar.topOffset as needed in order to avoid + // the traffic lights. Otherwise, position the + // sidebar by the top of the application's bounds + // based on the presence of sidebar.top. + if (!kIsWeb && sidebar.topOffset > 0) ...[ + SizedBox(height: sidebar.topOffset), + ] else if (sidebar.top != null) ...[ + const SizedBox(height: 12), + ] else + const SizedBox.shrink(), + if (_sidebarScrollController.hasClients && + _sidebarScrollController.offset > 0.0) + Divider( + thickness: 1, + height: 1, + color: dividerColor), + if (sidebar.top != null && + constraints.maxHeight > 81) + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + ), + child: sidebar.top!, ), - child: sidebar.top!, - ), - Expanded( - child: MacosScrollbar( - controller: _sidebarScrollController, - child: Padding( - padding: sidebar.padding, - child: sidebar.builder( - context, - _sidebarScrollController, + Expanded( + child: MacosScrollbar( + controller: _sidebarScrollController, + child: Padding( + padding: sidebar.padding, + child: sidebar.builder( + context, + _sidebarScrollController, + ), ), ), ), - ), - if (sidebar.bottom != null && - constraints.maxHeight > 141) - Padding( - padding: const EdgeInsets.all(16.0), - child: sidebar.bottom!, - ), - ], + if (sidebar.bottom != null && + constraints.maxHeight > 141) + Padding( + padding: const EdgeInsets.all(16.0), + child: sidebar.bottom!, + ), + ], + ), ), ), ), From 873807ae7cedbf6cb46d1403933ec266e5727f20 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sat, 7 Oct 2023 19:39:03 +0200 Subject: [PATCH 91/94] =?UTF-8?q?make=20sidebar=20items=20adapt=20their=20?= =?UTF-8?q?color=20based=20on=20the=20user=E2=80=99s=20selected=20system?= =?UTF-8?q?=20accent=20color?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/src/layout/sidebar/sidebar_items.dart | 163 +++++++++++++++++----- 1 file changed, 131 insertions(+), 32 deletions(-) diff --git a/lib/src/layout/sidebar/sidebar_items.dart b/lib/src/layout/sidebar/sidebar_items.dart index 65b3dadb..e24f90d3 100644 --- a/lib/src/layout/sidebar/sidebar_items.dart +++ b/lib/src/layout/sidebar/sidebar_items.dart @@ -1,4 +1,5 @@ import 'package:macos_ui/macos_ui.dart'; +import 'package:macos_ui/src/enums/accent_color.dart'; import 'package:macos_ui/src/library.dart'; const Duration _kExpand = Duration(milliseconds: 200); @@ -79,7 +80,8 @@ class SidebarItems extends StatelessWidget { /// The color to paint the item when it's selected. /// - /// If null, [MacosThemeData.primaryColor] is used. + /// If null, the color is chosen automatically based on the user’s selected + /// system accent color and whether the sidebar is in the main window. final Color? selectedColor; /// The color to paint the item when it's unselected. @@ -97,6 +99,21 @@ class SidebarItems extends StatelessWidget { /// Defaults to [SystemMouseCursors.basic]. final MouseCursor? cursor; + /// The user’s selected system accent color. + AccentColor get _accentColor => + AccentColorListener.instance.currentAccentColor ?? AccentColor.blue; + + /// Returns the sidebar item’s selected color. + Color _getColor(BuildContext context) { + final isMainWindow = WindowMainStateListener.instance.isMainWindow; + + return _ColorProvider.getSelectedColor( + accentColor: _accentColor, + isDarkModeEnabled: MacosTheme.of(context).brightness.isDark, + isWindowMain: isMainWindow, + ); + } + List get _allItems { List result = []; for (var element in items) { @@ -117,39 +134,50 @@ class SidebarItems extends StatelessWidget { final theme = MacosTheme.of(context); return MacosIconTheme.merge( data: const MacosIconThemeData(size: 20), - child: _SidebarItemsConfiguration( - selectedColor: selectedColor ?? theme.primaryColor, - unselectedColor: unselectedColor ?? MacosColors.transparent, - shape: shape ?? _defaultShape, - itemSize: itemSize, - child: ListView( - controller: scrollController, - physics: const ClampingScrollPhysics(), - padding: EdgeInsets.all(10.0 - theme.visualDensity.horizontal), - children: List.generate(items.length, (index) { - final item = items[index]; - if (item.disclosureItems != null) { - return MouseRegion( - cursor: cursor!, - child: _DisclosureSidebarItem( - item: item, - selectedItem: _allItems[currentIndex], - onChanged: (item) { - onChanged(_allItems.indexOf(item)); - }, + child: StreamBuilder( + stream: AccentColorListener.instance.onChanged, + builder: (context, _) { + return StreamBuilder( + stream: WindowMainStateListener.instance.onChanged, + builder: (context, _) { + return _SidebarItemsConfiguration( + selectedColor: selectedColor ?? _getColor(context), + unselectedColor: unselectedColor ?? MacosColors.transparent, + shape: shape ?? _defaultShape, + itemSize: itemSize, + child: ListView( + controller: scrollController, + physics: const ClampingScrollPhysics(), + padding: + EdgeInsets.all(10.0 - theme.visualDensity.horizontal), + children: List.generate(items.length, (index) { + final item = items[index]; + if (item.disclosureItems != null) { + return MouseRegion( + cursor: cursor!, + child: _DisclosureSidebarItem( + item: item, + selectedItem: _allItems[currentIndex], + onChanged: (item) { + onChanged(_allItems.indexOf(item)); + }, + ), + ); + } + return MouseRegion( + cursor: cursor!, + child: _SidebarItem( + item: item, + selected: _allItems[currentIndex] == item, + onClick: () => onChanged(_allItems.indexOf(item)), + ), + ); + }), ), ); - } - return MouseRegion( - cursor: cursor!, - child: _SidebarItem( - item: item, - selected: _allItems[currentIndex] == item, - onClick: () => onChanged(_allItems.indexOf(item)), - ), - ); - }), - ), + }, + ); + }, ), ); } @@ -497,3 +525,74 @@ class __DisclosureSidebarItemState extends State<_DisclosureSidebarItem> ); } } + +class _ColorProvider { + /// Returns the selected color based on the provided parameters. + static Color getSelectedColor({ + required AccentColor accentColor, + required bool isDarkModeEnabled, + required bool isWindowMain, + }) { + if (isDarkModeEnabled) { + if (!isWindowMain) { + return const MacosColor.fromRGBO(76, 78, 65, 1.0); + } + + switch (accentColor) { + case AccentColor.blue: + return const MacosColor.fromRGBO(22, 105, 229, 0.749); + + case AccentColor.purple: + return const MacosColor.fromRGBO(204, 45, 202, 0.749); + + case AccentColor.pink: + return const MacosColor.fromRGBO(229, 74, 145, 0.749); + + case AccentColor.red: + return const MacosColor.fromRGBO(238, 64, 68, 0.749); + + case AccentColor.orange: + return const MacosColor.fromRGBO(244, 114, 0, 0.749); + + case AccentColor.yellow: + return const MacosColor.fromRGBO(233, 176, 0, 0.749); + + case AccentColor.green: + return const MacosColor.fromRGBO(76, 177, 45, 0.749); + + case AccentColor.graphite: + return const MacosColor.fromRGBO(129, 129, 122, 0.824); + } + } + + if (!isWindowMain) { + return const MacosColor.fromRGBO(213, 213, 208, 1.0); + } + + switch (accentColor) { + case AccentColor.blue: + return const MacosColor.fromRGBO(9, 129, 255, 0.749); + + case AccentColor.purple: + return const MacosColor.fromRGBO(162, 28, 165, 0.749); + + case AccentColor.pink: + return const MacosColor.fromRGBO(234, 81, 152, 0.749); + + case AccentColor.red: + return const MacosColor.fromRGBO(220, 32, 40, 0.749); + + case AccentColor.orange: + return const MacosColor.fromRGBO(245, 113, 0, 0.749); + + case AccentColor.yellow: + return const MacosColor.fromRGBO(240, 180, 2, 0.749); + + case AccentColor.green: + return const MacosColor.fromRGBO(66, 174, 33, 0.749); + + case AccentColor.graphite: + return const MacosColor.fromRGBO(174, 174, 167, 0.847); + } + } +} From 2b1dba16c00ea67262750cffe8ba026f46c12125 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha Date: Sat, 7 Oct 2023 19:44:29 +0200 Subject: [PATCH 92/94] update changelog entry for version 2.0.1 --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f27d69cf..981068e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ ## [2.0.1] ### 🔄 Updated 🔄 * `PushButton` has received a facelift. It now mimics the look and feel of native macOS buttons more closely. - * **Note:** As a result, its `pressedOpacity` property and the `PushButtonTheme` class have been deprecated. + * **Note:** As a result, its `pressedOpacity` property and the `PushButtonTheme` class have been deprecated. +* Fixed a bug that caused the sidebar to appear darker than intended. +* `SidebarItems` has now respects the user’s selected accent color and mimics the look of macOS’ sidebar items more closely. ## [2.0.0] ### 🚨 Breaking Changes 🚨 From 5e49ad66e1d4c77aadc605a91bfc79bdb178fe74 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha <86920182+Adrian-Samoticha@users.noreply.github.com> Date: Mon, 30 Oct 2023 12:42:32 +0100 Subject: [PATCH 93/94] automatic changes --- pubspec.lock | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 62a3f534..b2e287b4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -223,18 +223,18 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.15" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.2.0" meta: dependency: transitive description: @@ -364,10 +364,10 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.9.1" stack_trace: dependency: transitive description: @@ -456,14 +456,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" - web: - dependency: transitive - description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 - url: "https://pub.dev" - source: hosted - version: "0.1.4-beta" web_socket_channel: dependency: transitive description: From 1f563c0498ba37e030288de9ddcc5f41dd94d782 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha <86920182+Adrian-Samoticha@users.noreply.github.com> Date: Mon, 30 Oct 2023 13:01:59 +0100 Subject: [PATCH 94/94] bump version to 2.0.3 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 8d81c2da..983e4bed 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.2 +version: 2.0.3 homepage: "https://macosui.dev" repository: "https://github.com/GroovinChip/macos_ui"