Skip to content

Commit e3d4cee

Browse files
committed
feat: add BorderBackgroundColor to ButtonControl and honour explicit Width in layouts
Add BorderBackgroundColor property and builder method for button border cells. Respect explicit Width on controls in VerticalStackLayout, WindowContentLayout, ScrollablePanelControl, and ProgressBarControl instead of always stretching.
1 parent 0093ec2 commit e3d4cee

6 files changed

Lines changed: 60 additions & 15 deletions

File tree

SharpConsoleUI/Builders/ButtonBuilder.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public sealed class ButtonBuilder : IControlBuilder<ButtonControl>
3838
private Color? _disabledBackgroundColor;
3939
private Color? _disabledForegroundColor;
4040
private Color? _borderColor;
41+
private Color? _borderBackgroundColor;
4142
private ButtonBorderStyle _borderStyle = ButtonBorderStyle.None;
4243
private EventHandler<ButtonControl>? _clickHandler;
4344
private WindowEventHandler<ButtonControl>? _clickWithWindowHandler;
@@ -293,6 +294,15 @@ public ButtonBuilder WithBorderColor(Color color)
293294
return this;
294295
}
295296

297+
/// <summary>
298+
/// Sets the background color for border cells. When null, uses the button's background color.
299+
/// </summary>
300+
public ButtonBuilder WithBorderBackgroundColor(Color color)
301+
{
302+
_borderBackgroundColor = color;
303+
return this;
304+
}
305+
296306
public ButtonBuilder WithColors(Color foregroundColor, Color backgroundColor)
297307
{
298308
_foregroundColor = foregroundColor;
@@ -407,6 +417,7 @@ public ButtonControl Build()
407417
if (_disabledBackgroundColor.HasValue) button.DisabledBackgroundColor = _disabledBackgroundColor.Value;
408418
if (_disabledForegroundColor.HasValue) button.DisabledForegroundColor = _disabledForegroundColor.Value;
409419
if (_borderColor.HasValue) button.BorderColor = _borderColor.Value;
420+
if (_borderBackgroundColor.HasValue) button.BorderBackgroundColor = _borderBackgroundColor.Value;
410421

411422
// Attach standard handler
412423
if (_clickHandler != null)

SharpConsoleUI/Controls/ButtonControl.cs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public class ButtonControl : BaseControl, IInteractiveControl, IFocusableControl
4949
private Color? _disabledBackgroundColor;
5050
private Color? _disabledForegroundColor;
5151
private Color? _borderColor;
52+
private Color? _borderBackgroundColor;
5253

5354
/// <summary>
5455
/// Initializes a new instance of the ButtonControl class with default settings.
@@ -163,6 +164,16 @@ public Color? BorderColor
163164
set => SetProperty(ref _borderColor, value);
164165
}
165166

167+
/// <summary>
168+
/// Gets or sets the background color for border cells.
169+
/// When null, uses the button's resolved background color.
170+
/// </summary>
171+
public Color? BorderBackgroundColor
172+
{
173+
get => _borderBackgroundColor;
174+
set => SetProperty(ref _borderBackgroundColor, value);
175+
}
176+
166177
/// <inheritdoc/>
167178
public bool ProcessKey(ConsoleKeyInfo key)
168179
{
@@ -334,6 +345,7 @@ public override void PaintDOM(CharacterBuffer buffer, LayoutRect bounds, LayoutR
334345
}
335346

336347
Color borderFg = _borderColor ?? foregroundColor;
348+
Color borderBg = _borderBackgroundColor ?? backgroundColor;
337349
int targetWidth = bounds.Width - Margin.Left - Margin.Right;
338350
if (targetWidth <= 0) return;
339351

@@ -389,35 +401,35 @@ public override void PaintDOM(CharacterBuffer buffer, LayoutRect bounds, LayoutR
389401
var box = _borderStyle == ButtonBorderStyle.Rounded ? BoxChars.Rounded : BoxChars.Single;
390402
if (row == 0)
391403
{
392-
buffer.SetNarrowCell(bx, y, box.TopLeft, borderFg, backgroundColor);
404+
buffer.SetNarrowCell(bx, y, box.TopLeft, borderFg, borderBg);
393405
for (int x = 1; x < buttonWidth - 1; x++)
394-
buffer.SetNarrowCell(bx + x, y, box.Horizontal, borderFg, backgroundColor);
395-
buffer.SetNarrowCell(bx + buttonWidth - 1, y, box.TopRight, borderFg, backgroundColor);
406+
buffer.SetNarrowCell(bx + x, y, box.Horizontal, borderFg, borderBg);
407+
buffer.SetNarrowCell(bx + buttonWidth - 1, y, box.TopRight, borderFg, borderBg);
396408
}
397409
else if (row == 2)
398410
{
399-
buffer.SetNarrowCell(bx, y, box.BottomLeft, borderFg, backgroundColor);
411+
buffer.SetNarrowCell(bx, y, box.BottomLeft, borderFg, borderBg);
400412
for (int x = 1; x < buttonWidth - 1; x++)
401-
buffer.SetNarrowCell(bx + x, y, box.Horizontal, borderFg, backgroundColor);
402-
buffer.SetNarrowCell(bx + buttonWidth - 1, y, box.BottomRight, borderFg, backgroundColor);
413+
buffer.SetNarrowCell(bx + x, y, box.Horizontal, borderFg, borderBg);
414+
buffer.SetNarrowCell(bx + buttonWidth - 1, y, box.BottomRight, borderFg, borderBg);
403415
}
404416
else
405417
{
406-
buffer.SetNarrowCell(bx, y, box.Vertical, borderFg, backgroundColor);
418+
buffer.SetNarrowCell(bx, y, box.Vertical, borderFg, borderBg);
407419
string padded = $" {new string(' ', leftInnerPad)}{text}{new string(' ', rightInnerPad)} ";
408420
var cells = Parsing.MarkupParser.Parse(padded, foregroundColor, backgroundColor);
409421
buffer.WriteCellsClipped(bx + 1, y, cells, clipRect);
410-
buffer.SetNarrowCell(bx + buttonWidth - 1, y, box.Vertical, borderFg, backgroundColor);
422+
buffer.SetNarrowCell(bx + buttonWidth - 1, y, box.Vertical, borderFg, borderBg);
411423
}
412424
}
413425
else if (_borderStyle == ButtonBorderStyle.Pipe)
414426
{
415427
// │ text │
416-
buffer.SetNarrowCell(bx, y, BoxChars.Single.Vertical, borderFg, backgroundColor);
428+
buffer.SetNarrowCell(bx, y, BoxChars.Single.Vertical, borderFg, borderBg);
417429
string padded = $" {new string(' ', leftInnerPad)}{text}{new string(' ', rightInnerPad)} ";
418430
var cells = Parsing.MarkupParser.Parse(padded, foregroundColor, backgroundColor);
419431
buffer.WriteCellsClipped(bx + 1, y, cells, clipRect);
420-
buffer.SetNarrowCell(bx + buttonWidth - 1, y, BoxChars.Single.Vertical, borderFg, backgroundColor);
432+
buffer.SetNarrowCell(bx + buttonWidth - 1, y, BoxChars.Single.Vertical, borderFg, borderBg);
421433
}
422434
else
423435
{

SharpConsoleUI/Controls/ProgressBarControl.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,12 @@ public override LayoutSize MeasureDOM(LayoutConstraints constraints)
276276
int height = contentHeight + Margin.Top + Margin.Bottom;
277277

278278
int contentWidth;
279-
if (_barWidth.HasValue)
279+
if (Width.HasValue)
280+
{
281+
// Explicit control width set — use it directly
282+
contentWidth = Width.Value - Margin.Left - Margin.Right;
283+
}
284+
else if (_barWidth.HasValue)
280285
{
281286
contentWidth = CalculateContentWidth(_barWidth.Value);
282287
}

SharpConsoleUI/Controls/ScrollablePanelControl/ScrollablePanelControl.Rendering.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,16 @@ public override void PaintDOM(CharacterBuffer buffer, LayoutRect bounds, LayoutR
209209
childNode.Measure(constraints);
210210
int childHeight = childNode.DesiredSize.Height;
211211

212+
// Respect explicit Width on child controls
213+
int childWidth = (child.Width.HasValue && child.Width.Value < contentWidth)
214+
? child.Width.Value
215+
: contentWidth;
216+
212217
// Register child bounds for cursor position lookups (even if off-viewport)
213218
var childBoundsForCursor = new LayoutRect(
214219
contentOriginX,
215220
contentOriginY + currentY,
216-
contentWidth,
221+
childWidth,
217222
childHeight);
218223
renderer?.UpdateChildBounds(child, childBoundsForCursor);
219224

@@ -223,7 +228,7 @@ public override void PaintDOM(CharacterBuffer buffer, LayoutRect bounds, LayoutR
223228
var childBounds = new LayoutRect(
224229
contentOriginX,
225230
contentOriginY + currentY,
226-
contentWidth,
231+
childWidth,
227232
childHeight);
228233

229234
// Arrange in screen coordinates (so AbsoluteBounds are correct)

SharpConsoleUI/Layout/VerticalStackLayout.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,17 @@ public void ArrangeChildren(LayoutNode node, LayoutRect finalRect)
185185
break;
186186
case HorizontalAlignment.Stretch:
187187
default:
188-
childWidth = finalRect.Width;
189-
childX = 0;
188+
// If control has explicit Width, honour it over full stretch
189+
if (child.Control?.Width.HasValue == true)
190+
{
191+
childWidth = child.DesiredSize.Width;
192+
childX = 0;
193+
}
194+
else
195+
{
196+
childWidth = finalRect.Width;
197+
childX = 0;
198+
}
190199
break;
191200
}
192201

SharpConsoleUI/Layout/WindowContentLayout.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,9 @@ public void ArrangeChildren(LayoutNode node, LayoutRect finalRect)
304304

305305
case HorizontalAlignment.Stretch:
306306
default:
307+
// If control has explicit Width, honour it over full stretch
308+
if (child.Control?.Width.HasValue == true)
309+
return (0, child.DesiredSize.Width);
307310
return (0, availableWidth);
308311
}
309312
}

0 commit comments

Comments
 (0)