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
10 changes: 6 additions & 4 deletions src/CCWrapper/Views/BottomBar.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
<TextBlock x:Name="CtxText" />
</StackPanel>
<Border Style="{StaticResource Sep}" />
<!-- ClickMode=Press: the click that closes the StaysOpen=false popup is the same mouse-down the 250 ms guard measures. A Button: it takes Win32 focus off the terminal's WebView2 when clicked, and has an automation peer for screen readers. -->
<!-- ClickMode=Press: the click that closes the popup is the same mouse-down the 250 ms guard measures. A Button: it takes Win32 focus off the terminal's WebView2 when clicked, and has an automation peer for screen readers. -->
<Button x:Name="UsageGroup" Style="{StaticResource Chip}" Padding="6,0" Margin="0" ToolTipOpening="OnUsageTip"
AutomationProperties.Name="Plan usage details" ClickMode="Press" GotKeyboardFocus="OnUsageFocus" Click="OnUsageClick">
<StackPanel Orientation="Horizontal">
Expand All @@ -53,9 +53,11 @@
<TextBlock x:Name="WeekText" MinWidth="28" />
</StackPanel>
</Button>
<!-- A Popup is its own window, so it may sit over the terminal's WebView2. Not inside UsageGroup: clicks in it would bubble to the toggle. -->
<Popup x:Name="UsagePopup" PlacementTarget="{Binding ElementName=UsageGroup}" Placement="Top" StaysOpen="False" Closed="OnUsageClosed">
<Border x:Name="UsagePanel" Style="{StaticResource UsagePanel}" Focusable="True" PreviewKeyDown="OnUsageKey">
<!-- A Popup is its own window, so it may sit over the terminal's WebView2. Not inside UsageGroup: clicks in it would bubble to the toggle.
StaysOpen=True: StaysOpen=False's mouse capture closed it only on clicks in other apps, not on clicks elsewhere in this window; the code closes it (OnWindowMouseDown, OnUsageFocusWithin).
AllowsTransparency: else the popup window paints black behind the rounded corners. -->
<Popup x:Name="UsagePopup" PlacementTarget="{Binding ElementName=UsageGroup}" Placement="Top" StaysOpen="True" AllowsTransparency="True" Closed="OnUsageClosed">
<Border x:Name="UsagePanel" Style="{StaticResource UsagePanel}" Focusable="True" PreviewKeyDown="OnUsageKey" IsKeyboardFocusWithinChanged="OnUsageFocusWithin">
<StackPanel x:Name="UsageBody" />
</Border>
</Popup>
Expand Down
25 changes: 24 additions & 1 deletion src/CCWrapper/Views/BottomBar.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public partial class BottomBar : UserControl
(double? Five, double? Week, DateTimeOffset? FiveReset, DateTimeOffset? WeekReset) _usageNow;
DateTime _usageClosedAt;
IInputElement? _usageFocus; // what had focus before the usage button took it, for Esc to give back
Window? _window;
CompactPrefs _compact = CompactPrefs.Load();
readonly DispatcherTimer _usageTimer = new() { Interval = TimeSpan.FromMinutes(30) };

Expand All @@ -48,6 +49,13 @@ public BottomBar()
CompactPrefs.Saved += OnCompactPrefs;
OnCompactPrefs();
_usageTimer.Start();
if ((_window = Window.GetWindow(this)) is { } w)
{
w.PreviewMouseDown += OnWindowMouseDown;
w.Deactivated += CloseUsage;
w.LocationChanged += CloseUsage;
w.SizeChanged += CloseUsage;
}
await ReadAccountAsync();
await CheckUsageAsync(force: false); // skipped when the start screen just checked
};
Expand All @@ -57,6 +65,13 @@ public BottomBar()
AppUpdate.Changed -= ShowUpdate;
CompactPrefs.Saved -= OnCompactPrefs;
_usageTimer.Stop();
if (_window is { } w)
{
w.PreviewMouseDown -= OnWindowMouseDown;
w.Deactivated -= CloseUsage;
w.LocationChanged -= CloseUsage;
w.SizeChanged -= CloseUsage;
}
};
_ = new SessionFollower<BarSource, BarData>(this, s => new BarSource(s), r => r.Read(), async d =>
{
Expand Down Expand Up @@ -109,9 +124,17 @@ void Render()
// Built when it opens: the "in 2h 10m" of the reset times is relative to now, not to the last render.
void OnUsageTip(object sender, ToolTipEventArgs e) => UsageGroup.ToolTip = _usageTip();

// StaysOpen=false closes the popup on the mouse-down of a click on the button itself, so a click right after a close must not reopen it.
// OnWindowMouseDown closes the popup on the mouse-down of a click on the button itself, so a click right after a close must not reopen it.
void OnUsageClick(object sender, RoutedEventArgs e) => OpenUsage();

// Any press in this window but in the popup closes it. The popup's own mouse events route through the window too. A click in a
// WebView2 (terminal, Browser panel) never reaches WPF: it takes the focus, and OnUsageFocusWithin closes the popup then.
void OnWindowMouseDown(object sender, MouseButtonEventArgs e) { if (!UsagePanel.IsMouseOver) CloseUsage(sender, e); }

void OnUsageFocusWithin(object sender, DependencyPropertyChangedEventArgs e) { if (!UsagePanel.IsKeyboardFocusWithin) UsagePopup.IsOpen = false; }

void CloseUsage(object? sender, EventArgs e) => UsagePopup.IsOpen = false;

void OpenUsage()
{
if (UsagePopup.IsOpen || DateTime.UtcNow - _usageClosedAt < TimeSpan.FromMilliseconds(250)) return;
Expand Down
4 changes: 2 additions & 2 deletions src/CCWrapper/Views/ComposeBox.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
</Style>
</TextBlock.Style>
</TextBlock>
<!-- A Popup is its own window, so it may sit over the terminal's WebView2 -->
<Popup x:Name="SlashPopup" PlacementTarget="{Binding ElementName=Input}" Placement="Top" StaysOpen="True">
<!-- A Popup is its own window, so it may sit over the terminal's WebView2. AllowsTransparency: else it paints black behind the rounded corners. -->
<Popup x:Name="SlashPopup" PlacementTarget="{Binding ElementName=Input}" Placement="Top" StaysOpen="True" AllowsTransparency="True">
<Border Background="{StaticResource Panel}" BorderBrush="{StaticResource ControlLine}" BorderThickness="1" CornerRadius="8"
Width="{Binding ActualWidth, ElementName=Input}" Margin="0,0,0,4">
<ListBox x:Name="SlashList" MaxHeight="280" Focusable="False" Background="Transparent" BorderThickness="0"
Expand Down