Skip to content

Commit

Permalink
feat: finalize battery SOC/time plot
Browse files Browse the repository at this point in the history
  • Loading branch information
timschneeb committed May 9, 2024
1 parent cb5e290 commit 9d8560c
Show file tree
Hide file tree
Showing 9 changed files with 497 additions and 41 deletions.
1 change: 0 additions & 1 deletion GalaxyBudsClient/FodyWeavers.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@
xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ReactiveUI/>
<AsyncErrorHandler/>
<Visualize/>
</Weavers>
1 change: 0 additions & 1 deletion GalaxyBudsClient/GalaxyBudsClient.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@
<PackageReference Include="SharpHook" Version="5.3.2" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
<PackageReference Include="Tmds.DBus" Version="0.16.0" />
<PackageReference Include="Visualize.Fody" Version="1.2.0" PrivateAssets="All" />
</ItemGroup>

<!-- Project references -->
Expand Down
1 change: 1 addition & 0 deletions GalaxyBudsClient/Interface/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public partial class MainWindow : StyledAppWindow
private static MainWindow? _instance;
public static MainWindow Instance => _instance ??= new MainWindow();

// TODO add loading spinners for pages?
public MainWindow()
{
InitializeComponent();
Expand Down
105 changes: 96 additions & 9 deletions GalaxyBudsClient/Interface/Pages/BatteryHistoryPage.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
xmlns:config="clr-namespace:GalaxyBudsClient.Model.Config"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:plot="clr-namespace:ScottPlot.Avalonia;assembly=ScottPlot.Avalonia"
xmlns:ic="clr-namespace:FluentIcons.Avalonia.Fluent;assembly=FluentIcons.Avalonia.Fluent"
mc:Ignorable="d" d:DesignWidth="800"
x:Class="GalaxyBudsClient.Interface.Pages.BatteryHistoryPage"
x:DataType="pages:BatteryHistoryPageViewModel"
Expand All @@ -17,34 +18,120 @@
<Design.DataContext>
<pages:BatteryHistoryPageViewModel />
</Design.DataContext>
<Grid RowDefinitions="Auto,Auto,*"
Margin="{StaticResource AppPageMargin}">

<Grid RowDefinitions="Auto,Auto,*,Auto"
Margin="{StaticResource AppPageMargin}">

<controls:CustomInfoBar Title="{ext:Translate {x:Static i18N:Keys.Hint}}"
Message="{ext:Translate {x:Static i18N:Keys.BattHistHint}}"
IsClosable="True"
IsOpen="{Binding !IsBatteryHistoryHintHidden, Source={x:Static config:Settings.Data}}"
Severity="Informational"
Closed="OnHintClosed"
Margin="0,0,0,8"/>
Margin="0,0,0,8" />

<StackPanel Grid.Row="1"
Spacing="8"
Orientation="Horizontal"
Margin="0,0,0,8">

<ComboBox ItemsSource="{Binding Source={ext:BatteryHistoryToolsBindingSource}}"
DisplayMemberBinding="{Binding ., Converter={StaticResource EnumDescriptionConverter}}"
SelectedValue="{Binding SelectedTool}"
Margin="0,0,8,0"
ToolTip.Tip="{ext:Translate {x:Static i18N:Keys.BattHistToolsTipTitle}}" />

<ComboBox ItemsSource="{Binding Source={ext:BatteryHistoryOverlaysBindingSource}}"
DisplayMemberBinding="{Binding ., Converter={StaticResource EnumDescriptionConverter}}"
SelectedValue="{Binding SelectedOverlay}"
Margin="0,0,8,0"
ToolTip.Tip="{ext:Translate {x:Static i18N:Keys.BattHistOverlayTipTitle}}"/>

<ComboBox ItemsSource="{Binding Source={ext:BatteryHistoryTimeSpansBindingSource}}"
DisplayMemberBinding="{Binding ., Converter={StaticResource EnumDescriptionConverter}}"
SelectedValue="{Binding SelectedTimeSpan}" />
SelectedValue="{Binding SelectedTimeSpan}"
Margin="0,0,8,0"
ToolTip.Tip="{ext:Translate {x:Static i18N:Keys.BattHistTimeSpanTipTitle}}"/>

<Button Command="{Binding DoReloadCommand}"
Margin="0,0,8,0"
ToolTip.Tip="{ext:Translate {x:Static i18N:Keys.FwSelectRefresh}}">
<Button.Content>
<ic:SymbolIcon Symbol="ArrowCounterclockwise" />
</Button.Content>
</Button>

<DropDownButton Name="HelpButton"
ToolTip.Tip="{ext:Translate {x:Static i18N:Keys.Help}}">
<Button.Content>
<ic:SymbolIcon Symbol="ChatHelp" />
</Button.Content>
<Button.Flyout>
<MenuFlyout Placement="Bottom">
<MenuItem Header="{ext:Translate {x:Static i18N:Keys.BattHistControlTipTitle}}"
Click="OnControlsHelpItemClicked">
<MenuItem.Icon>
<ic:SymbolIcon Symbol="CursorClick" />
</MenuItem.Icon>
</MenuItem>

<MenuItem Header="{ext:Translate {x:Static i18N:Keys.BattHistToolsTipTitle}}"
Click="OnToolsHelpItemClicked">
<MenuItem.Icon>
<ic:SymbolIcon Symbol="Toolbox" />
</MenuItem.Icon>
</MenuItem>

<MenuItem Header="{ext:Translate {x:Static i18N:Keys.BattHistOverlayTipTitle}}"
Click="OnOverlayHelpItemClicked">
<MenuItem.Icon>
<ic:SymbolIcon Symbol="RectangleLandscapeHintCopy" />
</MenuItem.Icon>
</MenuItem>
</MenuFlyout>
</Button.Flyout>
</DropDownButton>

<ui:TeachingTip Name="OverlayTip"
IsLightDismissEnabled="True"
TailVisibility="Collapsed"
Target="{Binding #HelpButton}"
Title="{ext:Translate {x:Static i18N:Keys.BattHistOverlayTipTitle}}"
Subtitle="{ext:Translate {x:Static i18N:Keys.BattHistOverlayTip}}" />

<ui:TeachingTip Name="ControlsTip"
IsLightDismissEnabled="True"
TailVisibility="Collapsed"
Target="{Binding #HelpButton}"
Title="{ext:Translate {x:Static i18N:Keys.BattHistControlTipTitle}}"
Subtitle="{ext:Translate {x:Static i18N:Keys.BattHistControlTip}}" />

<ui:TeachingTip Name="ToolsTip"
IsLightDismissEnabled="True"
TailVisibility="Collapsed"
Target="{Binding #HelpButton}"
Title="{ext:Translate {x:Static i18N:Keys.BattHistToolsTipTitle}}"
Subtitle="{ext:Translate {x:Static i18N:Keys.BattHistToolsTip}}" />
</StackPanel>

<!-- AvaPlot does not support bindings :( -->
<plot:AvaPlot Name="PlotControl"
IsVisible="{Binding !IsPlotLoading}"
PointerMoved="OnPlotPointerMoved"
PointerPressed="OnPlotPointerPressed"
PointerReleased="OnPlotPointerReleased"
Cursor="{Binding PlotCursor}"
Grid.Row="2" />

<ui:ProgressRing IsVisible="{Binding IsPlotLoading}"
Grid.Row="2" />


<StackPanel Grid.Row="3"
Margin="0,8,0,0"
Spacing="8"
Orientation="Horizontal">
<CheckBox
Content="{ext:Translate {x:Static i18N:Keys.BattHistShowLegend}}"
IsChecked="{Binding IsLegendVisible}" />
</StackPanel>
</Grid>
</UserControl>
123 changes: 122 additions & 1 deletion GalaxyBudsClient/Interface/Pages/BatteryHistoryPage.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,145 @@
using System;
using System.Linq;
using Avalonia.Input;
using Avalonia.Interactivity;
using FluentAvalonia.UI.Controls;
using GalaxyBudsClient.Interface.ViewModels.Pages;
using GalaxyBudsClient.Model.Config;
using GalaxyBudsClient.Model.Constants;
using GalaxyBudsClient.Utils.Extensions;
using ScottPlot;
using ScottPlot.AxisRules;
using ScottPlot.Control;
using Strings = GalaxyBudsClient.Generated.I18N.Strings;

namespace GalaxyBudsClient.Interface.Pages;

public partial class BatteryHistoryPage : BasePage<BatteryHistoryPageViewModel>
{
private bool _mouseIsDown;
private AxisLimits _axisLimitCache;
private Coordinates _mouseDownCoordinates;
private Coordinates _mouseNowCoordinates;
private readonly PlotActions _customActions;
private CoordinateRect MouseSelectionRect => new(_mouseDownCoordinates, _mouseNowCoordinates);

public BatteryHistoryPage()
{
InitializeComponent();

// Disable double-click benchmark action
_customActions = PlotActions.Standard();
_customActions.ToggleBenchmark = delegate { };
PlotControl.Interaction.Enable(_customActions);
}

protected override void OnInitialized()
{
ViewModel!.Plot = PlotControl.Plot;
ViewModel!.PlotControl = PlotControl;
base.OnInitialized();
}

private void OnHintClosed(InfoBar sender, InfoBarClosedEventArgs args)
{
Settings.Data.IsBatteryHistoryHintHidden = true;
}

private void OnOverlayHelpItemClicked(object? sender, RoutedEventArgs e)
{
OverlayTip.IsOpen = true;
}

private void OnControlsHelpItemClicked(object? sender, RoutedEventArgs e)
{
ControlsTip.IsOpen = true;
}

private void OnToolsHelpItemClicked(object? sender, RoutedEventArgs e)
{
ToolsTip.IsOpen = true;
}

public void OnPlotPointerMoved(object? sender, PointerEventArgs e)
{
if (!_mouseIsDown || ViewModel?.SelectedTool == BatteryHistoryTools.PanAndZoom)
return;

var pixel = e.ToPixel(PlotControl);
switch (ViewModel?.SelectedTool)
{
case BatteryHistoryTools.MeasureTime:
_mouseNowCoordinates = PlotControl.Plot.GetCoordinates(pixel.X, 0) with { Y = _axisLimitCache.Top };
break;
case BatteryHistoryTools.MeasureBattery:
_mouseNowCoordinates = PlotControl.Plot.GetCoordinates(0, pixel.Y) with { X = _axisLimitCache.Right };
break;
default:
return;
}

if (ViewModel?.SelectionRect is { } rect)
{
rect.CoordinateRect = MouseSelectionRect;
rect.IsVisible = true;
}

if (ViewModel?.MeasureAnnotation is { } annotation)
{
var timeSpan = TimeSpan.FromTicks(DateTime.FromOADate(_mouseNowCoordinates.X).Ticks - DateTime.FromOADate(_mouseDownCoordinates.X).Ticks);
annotation.Text = ViewModel.SelectedTool switch
{
BatteryHistoryTools.MeasureTime =>
string.Format(Strings.BattHistMeasureTimespanDisplay, timeSpan.ToString(timeSpan.Days > 0 ? Strings.BattHistMeasureTimespanUnitLong : "hh'h:'mm':'ss")),
BatteryHistoryTools.MeasureBattery =>
string.Format(Strings.BattHistMeasureDifferenceDisplay, $"{_mouseNowCoordinates.Y - _mouseDownCoordinates.Y:N0}%"),
_ => string.Empty
};
annotation.IsVisible = true;
}

PlotControl.Refresh();
}

public void OnPlotPointerPressed(object? sender, PointerPressedEventArgs e)
{
if(ViewModel?.SelectedTool == BatteryHistoryTools.PanAndZoom)
return;

var pixel = e.ToPixel(PlotControl);
_axisLimitCache = PlotControl.Plot.Axes.Rules.FirstOrDefault(r => r is MaximumBoundary) is MaximumBoundary maxBoundary
? maxBoundary.Limits
: new AxisLimits();

switch (ViewModel?.SelectedTool)
{
case BatteryHistoryTools.MeasureTime:
_mouseDownCoordinates = PlotControl.Plot.GetCoordinates(pixel.X, 0) with { Y = _axisLimitCache.Bottom };
break;
case BatteryHistoryTools.MeasureBattery:
_mouseDownCoordinates = PlotControl.Plot.GetCoordinates(0, pixel.Y) with { X = _axisLimitCache.Left };
break;
default:
return;
}

_mouseIsDown = true;
PlotControl.Interaction.Disable();
}

public void OnPlotPointerReleased(object? sender, PointerReleasedEventArgs e)
{
_mouseIsDown = false;
if (ViewModel?.SelectionRect is { } rect)
{
rect.IsVisible = false;
}

// Reset the mouse positions
_mouseDownCoordinates = Coordinates.NaN;
_mouseNowCoordinates = Coordinates.NaN;

// Update the plot
PlotControl.Refresh();
PlotControl.Interaction.Enable(_customActions);
}
}
Loading

0 comments on commit 9d8560c

Please sign in to comment.