diff --git a/lib/widgets/channel_colors.dart b/lib/widgets/channel_colors.dart index 388279453..405650313 100644 --- a/lib/widgets/channel_colors.dart +++ b/lib/widgets/channel_colors.dart @@ -91,21 +91,29 @@ class ChannelColorSwatch extends ColorSwatch { /// The channel icon on a plain-colored surface, such as white. /// - /// For the icon on a [barBackground]-colored surface, + /// For the icon on a [barBackgroundSolid]-colored surface, /// use [iconOnBarBackground] instead. Color get iconOnPlainBackground => this[ChannelColorVariant.iconOnPlainBackground]!; - /// The channel icon on a [barBackground]-colored surface. + /// The channel icon on a [barBackgroundSolid]-colored surface. /// /// For the icon on a plain surface, use [iconOnPlainBackground] instead. - /// This color is chosen to enhance contrast with [barBackground]: + /// This color is chosen to enhance contrast with [barBackgroundSolid]: /// Color get iconOnBarBackground => this[ChannelColorVariant.iconOnBarBackground]!; /// The background color of a bar representing a channel, like a recipient bar. /// /// Use this in the message list, the "Inbox" view, and the "Channels" view. - Color get barBackground => this[ChannelColorVariant.barBackground]!; + Color get barBackgroundSolid => this[ChannelColorVariant.barBackgroundSolid]!; + + /// The top color of a gradient-shaded bar representing a channel, + /// as in the inbox. + Color get barBackgroundGradientTop => this[ChannelColorVariant.barBackgroundGradientTop]!; + + /// The bottom color of a gradient-shaded bar representing a channel, + /// as in the inbox. + Color get barBackgroundGradientBottom => this[ChannelColorVariant.barBackgroundGradientBottom]!; static Map _computeLight(int base) { final baseAsColor = Color(base); @@ -113,6 +121,10 @@ class ChannelColorSwatch extends ColorSwatch { final clamped20to75 = clampLchLightness(baseAsColor, 20, 75); final clamped20to75AsHsl = HSLColor.fromColor(clamped20to75); + final barBackgroundSolid = LabColor.fromColor(const Color(0xfff9f9f9)) + .interpolate(LabColor.fromColor(clamped20to75), 0.22) + .toColor(); + return { ChannelColorVariant.base: baseAsColor, @@ -149,10 +161,12 @@ class ChannelColorSwatch extends ColorSwatch { // // which does ordinary RGB mixing. Investigate and send a PR? // TODO fix bug where our results differ from the replit's (see unit tests) - ChannelColorVariant.barBackground: - LabColor.fromColor(const Color(0xfff9f9f9)) - .interpolate(LabColor.fromColor(clamped20to75), 0.22) - .toColor(), + ChannelColorVariant.barBackgroundSolid: barBackgroundSolid, + + ChannelColorVariant.barBackgroundGradientTop: barBackgroundSolid, + + ChannelColorVariant.barBackgroundGradientBottom: + barBackgroundSolid.withValues(alpha: 0), }; } @@ -161,6 +175,10 @@ class ChannelColorSwatch extends ColorSwatch { final clamped20to75 = clampLchLightness(baseAsColor, 20, 75); + final barBackgroundSolid = LabColor.fromColor(const Color(0xff000000)) + .interpolate(LabColor.fromColor(clamped20to75), 0.38) + .toColor(); + return { // See comments in [_computeLight] about what these computations are based // on, and how the resulting values are a little off sometimes. The @@ -187,10 +205,13 @@ class ChannelColorSwatch extends ColorSwatch { // TODO fix bug where our results are unexpected (see unit tests) ChannelColorVariant.iconOnBarBackground: clamped20to75, - ChannelColorVariant.barBackground: - LabColor.fromColor(const Color(0xff000000)) - .interpolate(LabColor.fromColor(clamped20to75), 0.38) - .toColor(), + ChannelColorVariant.barBackgroundSolid: barBackgroundSolid, + + ChannelColorVariant.barBackgroundGradientTop: + barBackgroundSolid.withFadedAlpha(0.625), + + ChannelColorVariant.barBackgroundGradientBottom: + barBackgroundSolid.withFadedAlpha(0.125), }; } @@ -219,5 +240,7 @@ enum ChannelColorVariant { unreadCountBadgeBackground, iconOnPlainBackground, iconOnBarBackground, - barBackground, + barBackgroundSolid, + barBackgroundGradientTop, + barBackgroundGradientBottom, } diff --git a/lib/widgets/inbox.dart b/lib/widgets/inbox.dart index 607c3108e..ec04273ff 100644 --- a/lib/widgets/inbox.dart +++ b/lib/widgets/inbox.dart @@ -432,17 +432,15 @@ class InboxChannelHeaderItem extends StatelessWidget { } BoxDecoration _solidBackground(ChannelColorSwatch swatch) => - BoxDecoration(color: swatch.barBackground); + BoxDecoration(color: swatch.barBackgroundSolid); BoxDecoration _gradientBackground(ChannelColorSwatch swatch) => BoxDecoration( gradient: LinearGradient( begin: .topCenter, end: .bottomCenter, colors: [ - // TODO(design) is this the right color? - // https://chat.zulip.org/#narrow/channel/530-mobile-design/topic/channel.20folders.20in.20inbox.3A.20design/near/2422786 - swatch.barBackground, - swatch.barBackground.withValues(alpha: 0), + swatch.barBackgroundGradientTop, + swatch.barBackgroundGradientBottom, ], ), ); @@ -464,7 +462,7 @@ class InboxChannelHeaderItem extends StatelessWidget { child: InkWell( splashFactory: NoSplash.splashFactory, // TODO(design) this is ad hoc - highlightColor: swatch.barBackground.withFadedAlpha(0.5), + highlightColor: swatch.barBackgroundSolid.withFadedAlpha(0.5), // TODO use onRowTap to handle taps that are not on the collapse button. // Probably we should give the collapse button a 44px or 48px square // touch target: @@ -584,7 +582,7 @@ class InboxTopicItem extends StatelessWidget { child: InkWell( splashFactory: NoSplash.splashFactory, // TODO(design) this is ad hoc - highlightColor: swatch.barBackground.withFadedAlpha(0.25), + highlightColor: swatch.barBackgroundSolid.withFadedAlpha(0.25), onTap: () { final narrow = TopicNarrow(streamId, topic); Navigator.push(context, diff --git a/lib/widgets/message_list.dart b/lib/widgets/message_list.dart index 70242f5e6..7a722f3a8 100644 --- a/lib/widgets/message_list.dart +++ b/lib/widgets/message_list.dart @@ -470,7 +470,7 @@ abstract class _MessageListAppBar { case TopicNarrow(:final channelId): final subscription = store.subscriptions[channelId]; appBarBackgroundColor = - colorSwatchFor(context, subscription).barBackground; + colorSwatchFor(context, subscription).barBackgroundSolid; // All recipient headers will match this color; remove distracting line // (but are recipient headers even needed for topic narrows?) removeAppBarBottomBorder = true; @@ -1859,7 +1859,7 @@ class StreamMessageRecipientHeader extends StatelessWidget { final topic = message.conversation.topic; final swatch = colorSwatchFor(context, store.subscriptions[streamId]); - final backgroundColor = swatch.barBackground; + final backgroundColor = swatch.barBackgroundSolid; final iconColor = swatch.iconOnBarBackground; final Widget streamWidget; diff --git a/lib/widgets/topic_list.dart b/lib/widgets/topic_list.dart index 2eac29635..848bb8773 100644 --- a/lib/widgets/topic_list.dart +++ b/lib/widgets/topic_list.dart @@ -36,7 +36,7 @@ class TopicListPage extends StatelessWidget { final store = PerAccountStoreWidget.of(context); final zulipLocalizations = ZulipLocalizations.of(context); final appBarBackgroundColor = colorSwatchFor( - context, store.subscriptions[channelId]).barBackground; + context, store.subscriptions[channelId]).barBackgroundSolid; return PageRoot(child: Scaffold( appBar: ZulipAppBar( diff --git a/test/widgets/channel_colors_test.dart b/test/widgets/channel_colors_test.dart index c25707508..7555759db 100644 --- a/test/widgets/channel_colors_test.dart +++ b/test/widgets/channel_colors_test.dart @@ -222,10 +222,10 @@ void main() { runCheck(0xffacc25d, const Color(0xff8ea43e)); }); - test('barBackground', () { + test('barBackgroundSolid', () { void runCheck(int base, Color expected) { check(ChannelColorSwatch.light(base)) - .barBackground.isSameColorAs(expected); + .barBackgroundSolid.isSameColorAs(expected); } // Check against everything in ZULIP_ASSIGNMENT_COLORS @@ -263,6 +263,16 @@ void main() { runCheck(0xffa47462, const Color(0xffe7dad6)); runCheck(0xffacc25d, const Color(0xffe9edd6)); }); + + test('barBackgroundGradientTop', () { + check(ChannelColorSwatch.light(0xff76ce90)).barBackgroundGradientTop + .isSameColorAs(const Color(0xffddefe1)); + }); + + test('barBackgroundGradientBottom', () { + check(ChannelColorSwatch.light(0xff76ce90)).barBackgroundGradientBottom + .isSameColorAs(const Color(0x00ddefe1)); + }); }); group('dark', () { @@ -418,10 +428,10 @@ void main() { runCheck(0xffacc25d, const Color(0xffacc25d)); }); - test('barBackground', () { + test('barBackgroundSolid', () { void runCheck(int base, Color expected) { check(ChannelColorSwatch.dark(base)) - .barBackground.isSameColorAs(expected); + .barBackgroundSolid.isSameColorAs(expected); } // Check against everything in ZULIP_ASSIGNMENT_COLORS @@ -459,6 +469,17 @@ void main() { runCheck(0xffa47462, const Color(0xff3d2d27)); runCheck(0xffacc25d, const Color(0xff404627)); }); + + + test('barBackgroundGradientTop', () { + check(ChannelColorSwatch.dark(0xff76ce90)).barBackgroundGradientTop + .isSameColorAs(const Color(0x9f2e4935)); + }); + + test('barBackgroundGradientBottom', () { + check(ChannelColorSwatch.dark(0xff76ce90)).barBackgroundGradientBottom + .isSameColorAs(const Color(0x202e4935)); + }); }); test('lerp (different a, b)', () { @@ -476,8 +497,12 @@ void main() { Color.lerp(swatchA.iconOnPlainBackground, swatchB.iconOnPlainBackground, t)!), ChannelColorVariant.iconOnBarBackground => (check(result).iconOnBarBackground, Color.lerp(swatchA.iconOnBarBackground, swatchB.iconOnBarBackground, t)!), - ChannelColorVariant.barBackground => (check(result).barBackground, - Color.lerp(swatchA.barBackground, swatchB.barBackground, t)!), + ChannelColorVariant.barBackgroundSolid => (check(result).barBackgroundSolid, + Color.lerp(swatchA.barBackgroundSolid, swatchB.barBackgroundSolid, t)!), + ChannelColorVariant.barBackgroundGradientTop => (check(result).barBackgroundGradientTop, + Color.lerp(swatchA.barBackgroundGradientTop, swatchB.barBackgroundGradientTop, t)!), + ChannelColorVariant.barBackgroundGradientBottom => (check(result).barBackgroundGradientBottom, + Color.lerp(swatchA.barBackgroundGradientBottom, swatchB.barBackgroundGradientBottom, t)!), }; subject.isSameColorAs(expected); } diff --git a/test/widgets/checks.dart b/test/widgets/checks.dart index 09f6d13ac..271c7031d 100644 --- a/test/widgets/checks.dart +++ b/test/widgets/checks.dart @@ -26,7 +26,9 @@ extension ChannelColorSwatchChecks on Subject { Subject get unreadCountBadgeBackground => has((s) => s.unreadCountBadgeBackground, 'unreadCountBadgeBackground'); Subject get iconOnPlainBackground => has((s) => s.iconOnPlainBackground, 'iconOnPlainBackground'); Subject get iconOnBarBackground => has((s) => s.iconOnBarBackground, 'iconOnBarBackground'); - Subject get barBackground => has((s) => s.barBackground, 'barBackground'); + Subject get barBackgroundSolid => has((s) => s.barBackgroundSolid, 'barBackgroundSolid'); + Subject get barBackgroundGradientTop => has((s) => s.barBackgroundGradientTop, 'barBackgroundGradientTop'); + Subject get barBackgroundGradientBottom => has((s) => s.barBackgroundGradientBottom, 'barBackgroundGradientBottom'); } extension ComposeBoxStateChecks on Subject { diff --git a/test/widgets/message_list_test.dart b/test/widgets/message_list_test.dart index 92c3d2c3e..e3e8ecd38 100644 --- a/test/widgets/message_list_test.dart +++ b/test/widgets/message_list_test.dart @@ -1834,7 +1834,7 @@ void main() { find.descendant( of: find.byType(StreamMessageRecipientHeader), matching: find.byType(ColoredBox), - ))).color.isNotNull().isSameColorAs(swatch.barBackground); + ))).color.isNotNull().isSameColorAs(swatch.barBackgroundSolid); }); testWidgets('color of stream icon', (tester) async {