Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/CONNECTION_ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ A floating-style subtle reopen button reserves a dedicated 56 DIP row at
the content's top-left below the titlebar, never covering hosted hit targets.
The pane toggle retains a 40 DIP target and 16 DIP glyph in both states; focus
moves to the surviving toggle. Pane changes do not remount chat or reset drafts.
Both Workspace toggle states and the Settings toggle use the native
`SubtleButtonStyle` with zero padding for icon-only content. The style's default
text-button padding must not squeeze the glyph inside the fixed 40 DIP target.
The companion uses a native TitleBar titled OpenClaw Settings with shared claw
artwork. Search, Back, and Forward live beside the toggle in its stable sidebar
toolbar. The native pane reserves 56 DIP above its items, including in compact mode.
Expand Down
2 changes: 1 addition & 1 deletion src/OpenClaw.Tray.WinUI/Windows/HubWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@
AutomationProperties.AutomationId="SettingsTogglePane"
AutomationProperties.Name="Toggle navigation pane"
ToolTipService.ToolTip="Toggle navigation pane"
Width="40" Height="40" Margin="4,0,0,0"
Width="40" Height="40" Padding="0" Margin="4,0,0,0"
HorizontalAlignment="Left" VerticalAlignment="Top"
Style="{StaticResource SubtleButtonStyle}" Click="OnNavPaneToggleButtonClick">
<FontIcon Glyph="&#xE90C;" FontSize="16"/>
Expand Down
4 changes: 2 additions & 2 deletions src/OpenClaw.Tray.WinUI/Windows/WorkspaceWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -200,13 +200,13 @@
<Button x:Name="CollapsePaneButton" x:Uid="WorkspaceShell_TogglePane" Click="OnTogglePane"
AutomationProperties.AutomationId="WorkspaceTogglePane"
HorizontalAlignment="Left" VerticalAlignment="Top" Margin="4,8,0,0"
Width="40" Height="40" Style="{StaticResource SubtleButtonStyle}">
Width="40" Height="40" Padding="0" Style="{StaticResource SubtleButtonStyle}">
<FontIcon Glyph="&#xE90C;" FontSize="16"/>
</Button>
<Button x:Name="ReopenPaneButton" x:Uid="WorkspaceShell_TogglePane" Click="OnTogglePane"
AutomationProperties.AutomationId="WorkspaceReopenPane"
Visibility="Collapsed" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="4,8,0,0"
Width="40" Height="40" Style="{StaticResource SubtleButtonStyle}">
Width="40" Height="40" Padding="0" Style="{StaticResource SubtleButtonStyle}">
<FontIcon Glyph="&#xE90C;" FontSize="16"/>
</Button>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ public void PaneToggle_TargetAndGlyphSizesMatchAcrossPaneStates()
Assert.Equal("{StaticResource SubtleButtonStyle}", (string?)toggle.Attribute("Style"));
Assert.Equal("40", (string?)toggle.Attribute("Width"));
Assert.Equal("40", (string?)toggle.Attribute("Height"));
Assert.Equal("0", (string?)toggle.Attribute("Padding"));
var icon = Assert.Single(toggle.Descendants(), element => element.Name.LocalName == "FontIcon");
Assert.Equal("16", (string?)icon.Attribute("FontSize"));
}
Expand Down
67 changes: 67 additions & 0 deletions tests/OpenClaw.Tray.UITests/WorkspaceSessionLayoutTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Xml.Linq;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Markup;
using OpenClawTray.Presentation;
using OpenClawTray.Windows;
using Windows.Foundation;
Expand All @@ -9,6 +11,71 @@ namespace OpenClaw.Tray.UITests;
[Collection(UICollection.Name)]
public sealed class WorkspaceSessionLayoutTests(UIThreadFixture ui)
{
[Theory]
[InlineData("WorkspaceWindow.xaml", "CollapsePaneButton")]
[InlineData("WorkspaceWindow.xaml", "ReopenPaneButton")]
[InlineData("HubWindow.xaml", "NavPaneToggleButton")]
public async Task PaneToggle_LeavesRoundingRoomAroundUnconstrainedGlyph(string file, string name)
{
var root = Environment.GetEnvironmentVariable("OPENCLAW_REPO_ROOT")
?? throw new InvalidOperationException("OPENCLAW_REPO_ROOT is required for production markup proof.");
var document = XDocument.Load(Path.Combine(root, "src", "OpenClaw.Tray.WinUI", "Windows", file));
XNamespace x = "http://schemas.microsoft.com/winfx/2006/xaml";
var markup = new XElement(document.Descendants().Single(element => (string?)element.Attribute(x + "Name") == name));
// Mount the production control without its window event handler or localized tooltip.
markup.Attribute("Click")!.Remove();
markup.Attribute(x + "Uid")!.Remove();
markup.SetAttributeValue("Visibility", "Visible");
markup.SetAttributeValue(XNamespace.Xmlns + "x", x.NamespaceName);

await ui.RunOnUIAsync(async () =>
{
var button = Assert.IsType<Button>(XamlReader.Load(markup.ToString()));
var icon = Assert.IsType<FontIcon>(button.Content);
var reference = new FontIcon
{
Glyph = icon.Glyph,
FontFamily = icon.FontFamily,
FontSize = icon.FontSize,
IsTextScaleFactorEnabled = icon.IsTextScaleFactorEnabled
};
ui.Container.Children.Add(button);
ui.Container.Children.Add(reference);
try
{
await ui.YieldToRenderAsync();
foreach (var theme in new[] { ElementTheme.Light, ElementTheme.Dark })
{
button.RequestedTheme = theme;
button.UpdateLayout();
reference.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
var size = reference.DesiredSize;
Assert.True(size.Width > 0 && size.Height > 0);
Assert.InRange(button.ActualWidth, 39, 41);
Assert.InRange(button.ActualHeight, 39, 41);
var contentWidth = button.ActualWidth - button.Padding.Left - button.Padding.Right
- button.BorderThickness.Left - button.BorderThickness.Right;
// Leave a physical pixel on each side for fractional-DPI layout rounding.
var roundingRoom = 2 / button.XamlRoot.RasterizationScale;
Assert.True(contentWidth >= size.Width + roundingRoom,
$"Glyph width {size.Width} has no rounding room in content width {contentWidth} at raster scale {button.XamlRoot.RasterizationScale}.");
Assert.True(icon.ActualWidth >= size.Width,
$"Glyph width {size.Width} was clipped to {icon.ActualWidth} at raster scale {button.XamlRoot.RasterizationScale}.");
Assert.True(icon.ActualHeight >= size.Height,
$"Glyph height {size.Height} was clipped to {icon.ActualHeight}.");
var bounds = icon.TransformToVisual(button).TransformBounds(new Rect(0, 0, size.Width, size.Height));
Assert.True(bounds.Left >= 0 && bounds.Top >= 0 &&
bounds.Right <= button.ActualWidth && bounds.Bottom <= button.ActualHeight);
}
}
finally
{
ui.Container.Children.Remove(button);
ui.Container.Children.Remove(reference);
}
});
}

[Theory]
[InlineData(false, false)]
[InlineData(false, true)]
Expand Down
Loading