Skip to content

Commit c11330a

Browse files
larsbrubakerclaude
andcommitted
Scale remaining hard-coded widget geometry by DeviceScale
Survey follow-up to the sub-menu arrow fix: tree expander, splitter bar, menu separators, radio underline (now FillRectangle - the 5-arg Rectangle overload strokes a fixed ~1px outline), popup scroll allowance and border, tab corners, window radius, group box inset, radio/check box insets, chart text and radius, dialog button minimum width. ToggleSwitchView instead documents that its geometry is in caller units. Adds GuiDeviceScaleTests measuring rendered pixels at scale 1 vs 2. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent e137f9b commit c11330a

16 files changed

Lines changed: 505 additions & 39 deletions

File tree

Gui/Button/ThemedRadioTextButton.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,9 @@ public override void OnDraw(Graphics2D graphics2D)
150150
{
151151
if (Checked && DrawUnderline)
152152
{
153-
graphics2D.Rectangle(LocalBounds.Left, 0, LocalBounds.Right, 2, theme.PrimaryAccentColor);
153+
// Filled, not stroked - Rectangle() would outline the band with its default stroke and
154+
// leave it hollow as soon as the band is more than a pixel tall, which it is above scale one.
155+
graphics2D.FillRectangle(LocalBounds.Left, 0, LocalBounds.Right, 2 * DeviceScale, theme.PrimaryAccentColor);
154156
}
155157

156158
base.OnDraw(graphics2D);

Gui/Charting/SimpleChartWidget.cs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2023, Lars Brubaker
2+
Copyright (c) 2026, Lars Brubaker
33
All rights reserved.
44
55
Redistribution and use in source and binary forms, with or without
@@ -39,6 +39,13 @@ namespace Gui.Charting
3939
{
4040
public class SimpleChartWidget : GuiWidget
4141
{
42+
/// <summary>
43+
/// The unscaled size of every piece of text the chart draws. Multiply by
44+
/// <see cref="GuiWidget.DeviceScale"/> before handing it to a printer - point sizes are
45+
/// device pixels here, so they do not grow on their own.
46+
/// </summary>
47+
private const double DefaultPointSize = 12;
48+
4249
private ChartData chartData;
4350
private ChartOptions options;
4451
private ThemeConfig theme;
@@ -76,7 +83,7 @@ public override void OnDraw(Graphics2D graphics2D)
7683

7784
if (!string.IsNullOrEmpty(HoverValue.value))
7885
{
79-
graphics2D.DrawString(HoverValue.value, HoverValue.x, HoverValue.y);
86+
graphics2D.DrawString(HoverValue.value, HoverValue.x, HoverValue.y, pointSize: DefaultPointSize * DeviceScale);
8087
}
8188

8289
base.OnDraw(graphics2D);
@@ -104,8 +111,8 @@ public override void OnMouseMove(MouseEventArgs mouseEvent)
104111
newHoverText = chartData.Datasets[0].Data[index].ToString();
105112
}
106113
}
107-
var positionX = regionBounds.Right + 5;
108-
positionX = positionX > Width / 2 ? regionBounds.Left - 100 : positionX;
114+
var positionX = regionBounds.Right + 5 * DeviceScale;
115+
positionX = positionX > Width / 2 ? regionBounds.Left - 100 * DeviceScale : positionX;
109116
hoverValue = (positionX, regionBounds.Top, newHoverText);
110117
break;
111118
}
@@ -133,7 +140,7 @@ private void DrawBarChart(Graphics2D graphics2D)
133140
}
134141

135142
// draw the left widgets
136-
var pointSize = 12;
143+
var pointSize = DefaultPointSize * DeviceScale;
137144

138145
// print the 0 at the bottom
139146
var stringPrinter = new TypeFacePrinter($"{0}", pointSize);
@@ -155,7 +162,7 @@ private void DrawBarChart(Graphics2D graphics2D)
155162
var bounds = this.LocalBounds;
156163
bounds.Left += offset.X;
157164
bounds.Bottom += offset.Y;
158-
RenderBackground(graphics2D, bounds, theme.TextColor.WithAlpha(20), 5, 1, Color.Transparent);
165+
RenderBackground(graphics2D, bounds, theme.TextColor.WithAlpha(20), 5 * DeviceScale, 1, Color.Transparent);
159166

160167
var barWidth = bounds.Width / (maxSize.X * 2 + 1 + 2);
161168
var barOffset = barWidth * 2;
@@ -184,7 +191,7 @@ private void DrawBarChart(Graphics2D graphics2D)
184191
}
185192
}
186193

187-
RenderBackground(graphics2D, bounds, Color.Transparent, 5, 1, theme.TextColor);
194+
RenderBackground(graphics2D, bounds, Color.Transparent, 5 * DeviceScale, 1, theme.TextColor);
188195

189196
this.hoverAreas = hoverAreas;
190197
}

Gui/CheckBox/CheckBoxViewText.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,21 +82,24 @@ public override void OnDraw(Graphics2D graphics2D)
8282
{
8383
double bottom = LocalBounds.Bottom + (Height / 2 - CheckBoxWidth / 2);
8484

85+
// the gap between the left edge and the box, in points rather than device pixels
86+
double inset = 1 * DeviceScale;
87+
8588
// the check
8689
if (checkBox.Checked)
8790
{
88-
graphics2D.Line(new Vector2(1, CheckBoxWidth + bottom), new Vector2(1 + CheckBoxWidth, 0 + bottom), this.TextColor);
89-
graphics2D.Line(new Vector2(1, 0 + bottom), new Vector2(1 + CheckBoxWidth, CheckBoxWidth + bottom), this.TextColor);
91+
graphics2D.Line(new Vector2(inset, CheckBoxWidth + bottom), new Vector2(inset + CheckBoxWidth, 0 + bottom), this.TextColor);
92+
graphics2D.Line(new Vector2(inset, 0 + bottom), new Vector2(inset + CheckBoxWidth, CheckBoxWidth + bottom), this.TextColor);
9093
}
9194

9295
// the frame
93-
RectangleDouble clampedRect = new RectangleDouble(1, Math.Floor(0 + bottom), 1 + Math.Ceiling(CheckBoxWidth), Math.Ceiling(CheckBoxWidth + bottom));
96+
RectangleDouble clampedRect = new RectangleDouble(inset, Math.Floor(0 + bottom), inset + Math.Ceiling(CheckBoxWidth), Math.Ceiling(CheckBoxWidth + bottom));
9497
graphics2D.Rectangle(clampedRect, this.TextColor);
9598

9699
// extra frame
97100
if (checkBox.MouseDownOnWidget && checkBox.FirstWidgetUnderMouse)
98101
{
99-
clampedRect.Inflate(1);
102+
clampedRect.Inflate(1 * DeviceScale);
100103
graphics2D.Rectangle(clampedRect, this.TextColor);
101104
}
102105
}

Gui/GroupBox.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ namespace MatterHackers.Agg.UI
3434
public class GroupBox : GuiWidget
3535
{
3636
private GuiWidget groupBoxLabel;
37-
private double lineInset = 8.5;
37+
private double lineInset => 8.5 * DeviceScale;
3838
private GuiWidget clientArea;
3939

4040
public Color TextColor
@@ -119,8 +119,8 @@ public override void OnDraw(Graphics2D graphics2D)
119119
// right
120120
graphics2D.Line(localBounds.Left + Width - lineInset, localBounds.Bottom + lineInset, localBounds.Left + Width - lineInset, localBounds.Bottom + Height - lineInset, this.BorderColor);
121121
// top
122-
graphics2D.Line(localBounds.Left + lineInset, localBounds.Bottom + Height - lineInset, groupBoxLabel.BoundsRelativeToParent.Left - 2, localBounds.Bottom + Height - lineInset, this.BorderColor);
123-
graphics2D.Line(groupBoxLabel.BoundsRelativeToParent.Right + 2, localBounds.Bottom + Height - lineInset, localBounds.Left + Width - lineInset, localBounds.Bottom + Height - lineInset, this.BorderColor);
122+
graphics2D.Line(localBounds.Left + lineInset, localBounds.Bottom + Height - lineInset, groupBoxLabel.BoundsRelativeToParent.Left - 2 * DeviceScale, localBounds.Bottom + Height - lineInset, this.BorderColor);
123+
graphics2D.Line(groupBoxLabel.BoundsRelativeToParent.Right + 2 * DeviceScale, localBounds.Bottom + Height - lineInset, localBounds.Left + Width - lineInset, localBounds.Bottom + Height - lineInset, this.BorderColor);
124124

125125
base.OnDraw(graphics2D);
126126
}

Gui/Menu/DropDownList.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,8 @@ public MenuItem CreateSeparator()
264264

265265
MenuItems.Add(menuItem);
266266

267-
menuItem.MinimumSize = new Vector2(0, 3);
268-
menuItem.Height = 3;
267+
menuItem.MinimumSize = new Vector2(0, 3 * DeviceScale);
268+
menuItem.Height = 3 * DeviceScale;
269269

270270
return menuItem;
271271
}

Gui/Menu/Menu.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,9 @@ public Menu(GuiWidget view, Direction direction = Direction.Down, double maxHeig
8989

9090
public MenuItem AddHorizontalLine()
9191
{
92-
var menuItem = new MenuItem(new GuiWidget()
92+
// HorizontalLine already stretches and scales its own thickness with DeviceScale
93+
var menuItem = new MenuItem(new HorizontalLine(Color.LightGray)
9394
{
94-
HAnchor = HAnchor.Stretch,
95-
Height = 1,
96-
BackgroundColor = Color.LightGray,
9795
Margin = new BorderDouble(10, 1),
9896
VAnchor = VAnchor.Center,
9997
}, "HorizontalLine");

Gui/Menu/PopupWidget.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ public override void OnDraw(Graphics2D graphics2D)
125125
base.OnDraw(graphics2D);
126126

127127
var outline = new RoundedRect(LocalBounds, 0);
128-
graphics2D.Render(new Stroke(outline, BorderWidth * 2), BorderColor);
128+
graphics2D.Render(new Stroke(outline, BorderWidth * 2 * DeviceScale), BorderColor);
129129
}
130130

131131
public override void OnMouseDown(MouseEventArgs mouseEvent)
@@ -247,7 +247,8 @@ internal void MakeMenuHaveScroll(double maxHeight)
247247

248248
scrollingWindow.VAnchor = VAnchor.Absolute;
249249
scrollingWindow.Height = maxHeight;
250-
scrollingWindow.MinimumSize = new Vector2(Width + 15, 0);
250+
// leave room for the scroll bar the caller is about to get
251+
scrollingWindow.MinimumSize = new Vector2(Width + ScrollBar.ScrollBarWidth, 0);
251252
Width = scrollingWindow.Width;
252253
Height = maxHeight;
253254
scrollingWindow.ScrollArea.VAnchor = VAnchor.Fit;

Gui/RadioButton/RadioButtonGroup.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@ public void active_color(IColorType c)
8585

8686
public override void OnDraw(Graphics2D graphics2D)
8787
{
88-
RoundedRect backgroundRect = new RoundedRect(LocalBounds, 4);
88+
RoundedRect backgroundRect = new RoundedRect(LocalBounds, 4 * DeviceScale);
8989
graphics2D.Render(backgroundRect, backgroundColor.ToColor());
9090

91-
graphics2D.Render(new Stroke(backgroundRect), borderColor.ToColor());
91+
graphics2D.Render(new Stroke(backgroundRect, Math.Max(1, Math.Round(DeviceScale))), borderColor.ToColor());
9292

9393
base.OnDraw(graphics2D);
9494
}

Gui/RadioButton/RadioButtonViewText.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public RadioCircleWidget()
1212
{
1313
var boxWidth = RadioImage.BoxWidth;
1414

15-
this.MinimumSize = new Vector2(boxWidth + 1, boxWidth + 1);
15+
this.MinimumSize = new Vector2(boxWidth + 1 * DeviceScale, boxWidth + 1 * DeviceScale);
1616
this.DoubleBuffer = true;
1717
this.Margin = new BorderDouble(right: 10);
1818

Gui/Splitter.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2017, Lars Brubaker, John Lewin
2+
Copyright (c) 2026, Lars Brubaker, John Lewin
33
All rights reserved.
44
55
Redistribution and use in source and binary forms, with or without
@@ -43,10 +43,12 @@ public Splitter()
4343
splitterBar = new SplitterBar(this)
4444
{
4545
BackgroundColor = Color.Transparent,
46-
Width = 6,
46+
// the same 6 points ThemeConfig.SplitterWidth uses, but that is an instance
47+
// property and a Splitter is built without a theme
48+
Width = 6 * DeviceScale,
4749
};
4850

49-
SplitterDistance = 120;
51+
SplitterDistance = 120 * DeviceScale;
5052

5153
AddChild(Panel1);
5254
AddChild(splitterBar);

0 commit comments

Comments
 (0)