using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Layout;
using Avalonia.Media;
namespace Eu07.BlackBox.Views;
///
/// The pieces the window is built of, in one plain look: flat panels side by side, flat
/// buttons, a table of figures and tabs underlined.
///
internal static class UiKit
{
/// The gutter between the panels.
public const double Gap = 6;
public static readonly CornerRadius Small = new(2);
///
/// The theme's accent (the slider, the picked lines, the focus) in the orange of the viewer
/// instead of the system colour, for the window given and everything in it.
///
public static void Accent(Control control)
{
// As the starter's App.axaml sets them, for the viewer on its own.
(string Key, string Colour)[] accents =
{
("SystemAccentColor", "#177f00"), ("SystemAccentColorDark1", "#136B00"), ("SystemAccentColorDark2", "#0F5600"),
("SystemAccentColorDark3", "#0B4200"), ("SystemAccentColorLight1", "#2A9A0F"), ("SystemAccentColorLight2", "#41C400"),
("SystemAccentColorLight3", "#5FD42A")
};
foreach (var (key, colour) in accents)
control.Resources[key] = Color.Parse(colour);
control.Resources["AccentFillColorDefaultBrush"] = Palette.AccentFill;
control.Resources["AccentFillColorSecondaryBrush"] = Palette.AccentFillHover;
control.Resources["SystemControlHighlightListAccentLowBrush"] = Palette.AccentFill;
control.Resources["SystemControlHighlightListAccentMediumBrush"] = Palette.AccentFillHover;
control.Resources["ListBoxItemBackgroundSelected"] = Palette.AccentFill;
control.Resources["ListBoxItemBackgroundSelectedPointerOver"] = Palette.AccentFillHover;
control.Resources["ListBoxItemBackgroundSelectedPressed"] = Palette.AccentFill;
control.Resources["ListBoxItemBackgroundPointerOver"] = Palette.CardHover;
}
/// A panel with its title on a bar, as the group boxes of the starter; what goes right of the title, if anything.
public static Border Group(string title, Control content, Control? tag = null)
{
var bar = new DockPanel();
if (tag is not null)
{
DockPanel.SetDock(tag, Dock.Right);
tag.VerticalAlignment = VerticalAlignment.Center;
bar.Children.Add(tag);
}
bar.Children.Add(new TextBlock
{
Text = title,
FontSize = 12,
FontWeight = FontWeight.SemiBold,
Foreground = Palette.Text,
TextTrimming = TextTrimming.CharacterEllipsis,
VerticalAlignment = VerticalAlignment.Center
});
var header = new Border
{
Background = Palette.Header,
BorderBrush = Palette.CardBorder,
BorderThickness = new Thickness(0, 0, 0, 1),
Padding = new Thickness(8, 3),
MinHeight = 24,
Child = bar
};
var panel = new DockPanel();
DockPanel.SetDock(header, Dock.Top);
panel.Children.Add(header);
panel.Children.Add(content);
return Frame(panel);
}
/// A panel in the frame of the starter's group boxes, without a title.
public static Border Frame(Control content) => new()
{
Background = Palette.Panel,
BorderBrush = Palette.CardBorder,
BorderThickness = new Thickness(1),
CornerRadius = Small,
ClipToBounds = true,
Child = content
};
/// A flat panel the views sit on.
public static Border Card(Control content, Thickness? padding = null) => new()
{
Background = Palette.Card,
Padding = padding ?? new Thickness(0),
ClipToBounds = true,
Child = content
};
/// A button of the toolbar: text on the bar, a lighter ground under the pointer.
public static Button Button(string text, string? tip = null)
{
var button = new Button
{
Content = text,
Padding = new Thickness(10, 2),
MinHeight = 25,
CornerRadius = Small,
FontSize = 12,
VerticalContentAlignment = VerticalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
if (tip is not null)
ToolTip.SetTip(button, tip);
return button;
}
/// The button of playing.
public static Button PlayButton()
{
var button = new Button
{
Width = 32,
Height = 25,
CornerRadius = Small,
Background = Palette.AccentFill,
Padding = new Thickness(0),
BorderThickness = new Thickness(0),
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalContentAlignment = VerticalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
// As the starter's start button: its green, brighter under the pointer.
Hover(button, Palette.Accent, Brushes.White);
return button;
}
/// The glyph of play or pause.
public static Control PlayGlyph(bool playing)
{
var geometry = playing
? Geometry.Parse("M0,0 H4 V12 H0 Z M7,0 H11 V12 H7 Z")
: Geometry.Parse("M1,0 L11,6 L1,12 Z");
return new Avalonia.Controls.Shapes.Path
{
Data = geometry,
Fill = Brushes.White,
Width = 12,
Height = 12,
Stretch = Stretch.None,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
}
/// A toggle of the toolbar: on, its text takes the accent.
public static ToggleButton Toggle(string text, string? tip = null)
{
var toggle = new ToggleButton
{
Content = text,
Padding = new Thickness(10, 2),
MinHeight = 25,
CornerRadius = Small,
FontSize = 12,
VerticalContentAlignment = VerticalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
foreach (var state in new[] { "", "PointerOver", "Pressed" })
{
toggle.Resources["ToggleButtonBackgroundChecked" + state] = state == "PointerOver" ? Palette.AccentFillHover : Palette.AccentFill;
toggle.Resources["ToggleButtonForegroundChecked" + state] = Brushes.White;
}
if (tip is not null)
ToolTip.SetTip(toggle, tip);
return toggle;
}
// The theme takes its own colours under the pointer and when pressed, unless the control gives them.
private static void Hover(Control control, IBrush background, IBrush foreground)
{
foreach (var kind in new[] { "Button", "ToggleButton" })
{
control.Resources[kind + "BackgroundPointerOver"] = background;
control.Resources[kind + "BackgroundPressed"] = background;
control.Resources[kind + "ForegroundPointerOver"] = foreground;
control.Resources[kind + "ForegroundPressed"] = foreground;
control.Resources[kind + "BorderBrushPointerOver"] = Brushes.Transparent;
control.Resources[kind + "BorderBrushPressed"] = Brushes.Transparent;
}
}
/// A line of the table of figures: what it is on the left, its value on the right, a note under it.
public sealed class Tile : Border
{
private readonly TextBlock _value = new() { FontSize = 12, FontWeight = FontWeight.SemiBold, Foreground = Palette.Text, HorizontalAlignment = HorizontalAlignment.Right };
private readonly TextBlock _sub = new() { FontSize = 11, Foreground = Palette.FaintText, TextTrimming = TextTrimming.CharacterEllipsis };
public Tile(string caption)
{
BorderBrush = Palette.Grid;
BorderThickness = new Thickness(0, 0, 0, 1);
Padding = new Thickness(8, 4);
var grid = new Grid { ColumnDefinitions = new ColumnDefinitions("*,Auto"), RowDefinitions = new RowDefinitions("Auto,Auto") };
var name = new TextBlock { Text = caption, FontSize = 12, Foreground = Palette.DimText, VerticalAlignment = VerticalAlignment.Center };
Grid.SetColumn(_value, 1);
Grid.SetRow(_sub, 1);
Grid.SetColumnSpan(_sub, 2);
grid.Children.Add(name);
grid.Children.Add(_value);
grid.Children.Add(_sub);
Child = grid;
}
public void Show(string value, string? sub = null, IBrush? brush = null)
{
_value.Text = value;
_value.Foreground = brush ?? Palette.Text;
_sub.Text = sub ?? "";
_sub.IsVisible = !string.IsNullOrEmpty(sub);
}
}
/// Tabs side by side as the starter's: the one on on the panel's ground, with a bar of the accent on top.
public sealed class Segmented : Border
{
private readonly StackPanel _row = new() { Orientation = Orientation.Horizontal };
private int _selected = -1;
public event Action? SelectedChanged;
public Segmented(params string[] choices)
{
Background = Palette.Background;
BorderBrush = Palette.CardBorder;
BorderThickness = new Thickness(0, 0, 0, 1);
Child = _row;
for (int i = 0; i < choices.Length; i++)
{
int index = i;
var button = new Button
{
Content = choices[i],
Padding = new Thickness(14, 3),
MinHeight = 24,
CornerRadius = new CornerRadius(0),
FontSize = 12,
BorderThickness = new Thickness(0, 2, 0, 0)
};
button.Resources["ButtonBackgroundPointerOver"] = Palette.CardHover;
button.Resources["ButtonBackgroundPressed"] = Palette.CardHover;
button.Resources["ButtonForegroundPointerOver"] = Palette.Text;
button.Resources["ButtonForegroundPressed"] = Palette.Text;
button.Click += (_, _) => Selected = index;
_row.Children.Add(button);
}
Selected = 0;
}
public int Selected
{
get => _selected;
set
{
if (value == _selected)
return;
_selected = value;
for (int i = 0; i < _row.Children.Count; i++)
{
var button = (Button)_row.Children[i];
bool on = i == value;
var line = on ? Palette.Accent : Brushes.Transparent;
button.Background = on ? Palette.Panel : Brushes.Transparent;
button.BorderBrush = line;
button.Resources["ButtonBorderBrushPointerOver"] = line;
button.Resources["ButtonBorderBrushPressed"] = line;
button.Foreground = on ? Palette.Text : Palette.DimText;
}
SelectedChanged?.Invoke(value);
}
}
}
}