Skip to content

Commit 6f6ea2b

Browse files
committed
Add customizable chat styling properties
Expose new styling bindable properties for ChatView (ChatBackgroundColor, SendButtonBackgroundColor, SendButtonTextColor, InputBarBackgroundColor, InputBarBorderColor, BubbleFontSize, BubbleFontFamily, TimestampFontSize, BubbleCornerRadius) and wire them into the UI. Make messageArea a field and add OnChatBackgroundColorChanged to update the messages area background. Update ChatBubbleView to use the new font, timestamp size and corner-radius properties (and change link helper to instance method), and add corresponding properties on ChatInputBar (send button colors, bar background and border). Update README and SKILLS documentation to document the new options.
1 parent f447a40 commit 6f6ea2b

7 files changed

Lines changed: 182 additions & 10 deletions

File tree

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,17 @@ A modern chat UI control with message bubbles, typing indicators, load-more pagi
382382
| MyTextColor | Color | Black | Local user text color |
383383
| OtherBubbleColor | Color | White | Default other-user bubble color |
384384
| OtherTextColor | Color | Black | Other-user text color |
385+
| ChatBackgroundColor | Color? | null | Background color for the messages area |
386+
| BubbleFontSize | double | 15 | Font size for bubble text |
387+
| BubbleFontFamily | string? | null | Font family for bubble text |
388+
| TimestampFontSize | double | 11 | Font size for timestamps |
389+
| BubbleCornerRadius | double | 18 | Corner radius for bubbles (tail stays at 4) |
385390
| PlaceholderText | string | "Type a message..." | Input placeholder |
386391
| SendButtonText | string | "Send" | Send button label |
392+
| SendButtonBackgroundColor | Color | #007AFF | Send button background color |
393+
| SendButtonTextColor | Color | White | Send button text color |
394+
| InputBarBackgroundColor | Color | #F5F5F5 | Input bar background color |
395+
| InputBarBorderColor | Color | #E0E0E0 | Input bar top border color |
387396
| IsInputBarVisible | bool | true | Show/hide the input bar |
388397
| ShowTypingIndicator | bool | true | Enable typing notifications |
389398
| TypingParticipants | IList\<ChatParticipant\> | null | Currently typing participants |

SKILLS/shiny-controls/chatview.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,17 @@ public class ChatParticipant
8383
| `MyTextColor` | `Color` / `string` | `Black` | Local user text color |
8484
| `OtherBubbleColor` | `Color` / `string` | `White` | Default other-user bubble color (overridden by participant's `BubbleColor`) |
8585
| `OtherTextColor` | `Color` / `string` | `Black` | Other-user text color |
86+
| `ChatBackgroundColor` | `Color?` / `string?` | `null` | Background color for the messages area |
87+
| `BubbleFontSize` | `double` | `15` | Font size for message bubble text |
88+
| `BubbleFontFamily` | `string?` | `null` | Font family for message bubble text |
89+
| `TimestampFontSize` | `double` | `11` | Font size for timestamp labels |
90+
| `BubbleCornerRadius` | `double` | `18` | Corner radius for message bubbles (tail corner remains 4) |
8691
| `PlaceholderText` | `string` | `"Type a message..."` | Input field placeholder |
8792
| `SendButtonText` | `string` | `"Send"` | Send button label |
93+
| `SendButtonBackgroundColor` | `Color` / `string` | `#007AFF` | Send button background color |
94+
| `SendButtonTextColor` | `Color` / `string` | `White` | Send button text color |
95+
| `InputBarBackgroundColor` | `Color` / `string` | `#F5F5F5` | Input bar background color |
96+
| `InputBarBorderColor` | `Color` / `string` | `#E0E0E0` | Input bar top border/separator color |
8897
| `IsInputBarVisible` | `bool` | `true` | Show/hide the entire input bar |
8998
| `ShowTypingIndicator` | `bool` | `true` | Enable/disable typing indicator |
9099
| `TypingParticipants` | `IList<ChatParticipant>` | `null` | Currently typing participants (do not include "me") |

src/Shiny.Maui.Controls/Chat/ChatView.Commands.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ namespace Shiny.Maui.Controls.Chat;
66

77
public partial class ChatView
88
{
9+
void OnChatBackgroundColorChanged(Color? color)
10+
{
11+
messageArea.BackgroundColor = color;
12+
}
13+
914
void OnMessagesChanged()
1015
{
1116
if (observedCollection is not null)

src/Shiny.Maui.Controls/Chat/ChatView.Properties.cs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,128 @@ public IList<ChatBubbleTool>? MyBubbleToolItems
373373
set => SetValue(MyBubbleToolItemsProperty, value);
374374
}
375375

376+
// Chat background
377+
public static readonly BindableProperty ChatBackgroundColorProperty = BindableProperty.Create(
378+
nameof(ChatBackgroundColor),
379+
typeof(Color),
380+
typeof(ChatView),
381+
null,
382+
propertyChanged: (b, _, n) => ((ChatView)b).OnChatBackgroundColorChanged(n as Color));
383+
384+
public Color? ChatBackgroundColor
385+
{
386+
get => (Color?)GetValue(ChatBackgroundColorProperty);
387+
set => SetValue(ChatBackgroundColorProperty, value);
388+
}
389+
390+
// Send button styling
391+
public static readonly BindableProperty SendButtonBackgroundColorProperty = BindableProperty.Create(
392+
nameof(SendButtonBackgroundColor),
393+
typeof(Color),
394+
typeof(ChatView),
395+
Color.FromArgb("#007AFF"),
396+
propertyChanged: (b, _, n) => ((ChatView)b).inputBar.SendButtonBackgroundColor = (Color)n);
397+
398+
public Color SendButtonBackgroundColor
399+
{
400+
get => (Color)GetValue(SendButtonBackgroundColorProperty);
401+
set => SetValue(SendButtonBackgroundColorProperty, value);
402+
}
403+
404+
public static readonly BindableProperty SendButtonTextColorProperty = BindableProperty.Create(
405+
nameof(SendButtonTextColor),
406+
typeof(Color),
407+
typeof(ChatView),
408+
Colors.White,
409+
propertyChanged: (b, _, n) => ((ChatView)b).inputBar.SendButtonTextColor = (Color)n);
410+
411+
public Color SendButtonTextColor
412+
{
413+
get => (Color)GetValue(SendButtonTextColorProperty);
414+
set => SetValue(SendButtonTextColorProperty, value);
415+
}
416+
417+
// Input bar styling
418+
public static readonly BindableProperty InputBarBackgroundColorProperty = BindableProperty.Create(
419+
nameof(InputBarBackgroundColor),
420+
typeof(Color),
421+
typeof(ChatView),
422+
Color.FromArgb("#F5F5F5"),
423+
propertyChanged: (b, _, n) => ((ChatView)b).inputBar.BarBackgroundColor = (Color)n);
424+
425+
public Color InputBarBackgroundColor
426+
{
427+
get => (Color)GetValue(InputBarBackgroundColorProperty);
428+
set => SetValue(InputBarBackgroundColorProperty, value);
429+
}
430+
431+
public static readonly BindableProperty InputBarBorderColorProperty = BindableProperty.Create(
432+
nameof(InputBarBorderColor),
433+
typeof(Color),
434+
typeof(ChatView),
435+
Color.FromArgb("#E0E0E0"),
436+
propertyChanged: (b, _, n) => ((ChatView)b).inputBar.BarBorderColor = (Color)n);
437+
438+
public Color InputBarBorderColor
439+
{
440+
get => (Color)GetValue(InputBarBorderColorProperty);
441+
set => SetValue(InputBarBorderColorProperty, value);
442+
}
443+
444+
// Font properties
445+
public static readonly BindableProperty BubbleFontSizeProperty = BindableProperty.Create(
446+
nameof(BubbleFontSize),
447+
typeof(double),
448+
typeof(ChatView),
449+
15.0,
450+
propertyChanged: (b, _, _) => ((ChatView)b).RefreshBubbles());
451+
452+
public double BubbleFontSize
453+
{
454+
get => (double)GetValue(BubbleFontSizeProperty);
455+
set => SetValue(BubbleFontSizeProperty, value);
456+
}
457+
458+
public static readonly BindableProperty BubbleFontFamilyProperty = BindableProperty.Create(
459+
nameof(BubbleFontFamily),
460+
typeof(string),
461+
typeof(ChatView),
462+
null,
463+
propertyChanged: (b, _, _) => ((ChatView)b).RefreshBubbles());
464+
465+
public string? BubbleFontFamily
466+
{
467+
get => (string?)GetValue(BubbleFontFamilyProperty);
468+
set => SetValue(BubbleFontFamilyProperty, value);
469+
}
470+
471+
public static readonly BindableProperty TimestampFontSizeProperty = BindableProperty.Create(
472+
nameof(TimestampFontSize),
473+
typeof(double),
474+
typeof(ChatView),
475+
11.0,
476+
propertyChanged: (b, _, _) => ((ChatView)b).RefreshBubbles());
477+
478+
public double TimestampFontSize
479+
{
480+
get => (double)GetValue(TimestampFontSizeProperty);
481+
set => SetValue(TimestampFontSizeProperty, value);
482+
}
483+
484+
// Bubble corner radius
485+
public static readonly BindableProperty BubbleCornerRadiusProperty = BindableProperty.Create(
486+
nameof(BubbleCornerRadius),
487+
typeof(double),
488+
typeof(ChatView),
489+
18.0,
490+
propertyChanged: (b, _, _) => ((ChatView)b).RefreshBubbles());
491+
492+
public double BubbleCornerRadius
493+
{
494+
get => (double)GetValue(BubbleCornerRadiusProperty);
495+
set => SetValue(BubbleCornerRadiusProperty, value);
496+
}
497+
376498
// Haptic
377499
public static readonly BindableProperty UseFeedbackProperty = BindableProperty.Create(
378500
nameof(UseFeedback),

src/Shiny.Maui.Controls/Chat/ChatView.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public partial class ChatView : ContentView
1313
readonly Border toastPill;
1414
readonly Label toastNewMessagesLabel;
1515
readonly Label toastTypingLabel;
16+
readonly Grid messageArea;
1617
readonly FabMenu toolsMenu;
1718
readonly FabMenu bubbleToolsMenu;
1819
ChatMessage? activeBubbleToolMessage;
@@ -80,7 +81,7 @@ public ChatView()
8081
toastPill.GestureRecognizers.Add(pillTap);
8182

8283
// Messages area: CollectionView + toast pill overlay
83-
var messageArea = new Grid
84+
messageArea = new Grid
8485
{
8586
IsClippedToBounds = true,
8687
Children = { collectionView, toastPill }

src/Shiny.Maui.Controls/Chat/Internal/ChatBubbleView.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,7 @@ public ChatBubbleView(ChatView chatView, bool isMe)
7979

8080
textLabel = new Label
8181
{
82-
LineBreakMode = LineBreakMode.WordWrap,
83-
FontSize = 15
82+
LineBreakMode = LineBreakMode.WordWrap
8483
};
8584

8685
imageView = new Image
@@ -100,8 +99,6 @@ public ChatBubbleView(ChatView chatView, bool isMe)
10099
{
101100
Padding = new Thickness(12, 8),
102101
StrokeThickness = 0,
103-
StrokeShape = new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = 18 },
104-
105102
MaximumWidthRequest = 280,
106103
Content = defaultContentLayout
107104
};
@@ -258,10 +255,11 @@ void Configure(ChatMessage message)
258255
bubbleBorder.BackgroundColor = bubbleColor;
259256

260257
// Corner radius: rounded with smaller tail corner
261-
var tailRadius = isLast ? 4 : 18;
258+
var radius = chatView.BubbleCornerRadius;
259+
var tailRadius = isLast ? 4 : radius;
262260
bubbleBorder.StrokeShape = isMe
263-
? new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = new CornerRadius(18, 18, 18, tailRadius) }
264-
: new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = new CornerRadius(18, 18, tailRadius, 18) };
261+
? new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = new CornerRadius(radius, radius, radius, tailRadius) }
262+
: new Microsoft.Maui.Controls.Shapes.RoundRectangle { CornerRadius = new CornerRadius(radius, radius, tailRadius, radius) };
265263

266264
// Content: custom template, text, or image
267265
var template = chatView.MessageTemplateSelector?.SelectTemplate(message, this)
@@ -296,6 +294,8 @@ void Configure(ChatMessage message)
296294
textLabel.IsVisible = true;
297295
imageView.IsVisible = false;
298296
textLabel.TextColor = textColor;
297+
textLabel.FontSize = chatView.BubbleFontSize;
298+
textLabel.FontFamily = chatView.BubbleFontFamily;
299299
SetTextWithLinks(textLabel, message.Text ?? string.Empty, textColor);
300300
bubbleBorder.Padding = new Thickness(12, 8);
301301
}
@@ -305,6 +305,7 @@ void Configure(ChatMessage message)
305305

306306
// Timestamp
307307
timestampLabel.IsVisible = isLast;
308+
timestampLabel.FontSize = chatView.TimestampFontSize;
308309
if (isLast)
309310
timestampLabel.Text = ChatGroupHelper.FormatTimestamp(message.Timestamp);
310311

@@ -414,7 +415,7 @@ void OnToolsButtonClicked(object? sender, EventArgs e)
414415
return null;
415416
}
416417

417-
static void SetTextWithLinks(Label label, string text, Color textColor)
418+
void SetTextWithLinks(Label label, string text, Color textColor)
418419
{
419420
var matches = UrlRegex.Matches(text);
420421
if (matches.Count == 0)

src/Shiny.Maui.Controls/Chat/Internal/ChatInputBar.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class ChatInputBar : ContentView
77
readonly Button attachButton;
88
readonly Button toolsButton;
99
readonly Grid rootGrid;
10+
readonly BoxView separator;
1011

1112
public event Action<string>? SendRequested;
1213
public event Action? AttachRequested;
@@ -82,7 +83,7 @@ public ChatInputBar()
8283
};
8384

8485
// Top border line
85-
var separator = new BoxView
86+
separator = new BoxView
8687
{
8788
HeightRequest = 0.5,
8889
Color = Color.FromArgb("#E0E0E0"),
@@ -122,6 +123,30 @@ public string SendButtonText
122123
set => sendButton.Text = value;
123124
}
124125

126+
public Color SendButtonBackgroundColor
127+
{
128+
get => sendButton.BackgroundColor;
129+
set => sendButton.BackgroundColor = value;
130+
}
131+
132+
public Color SendButtonTextColor
133+
{
134+
get => sendButton.TextColor;
135+
set => sendButton.TextColor = value;
136+
}
137+
138+
public Color BarBackgroundColor
139+
{
140+
get => rootGrid.BackgroundColor;
141+
set => rootGrid.BackgroundColor = value;
142+
}
143+
144+
public Color BarBorderColor
145+
{
146+
get => separator.Color;
147+
set => separator.Color = value;
148+
}
149+
125150
public bool ShowAttachButton
126151
{
127152
get => attachButton.IsVisible;

0 commit comments

Comments
 (0)