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
49 changes: 49 additions & 0 deletions src/CUETools.Linux.App/Controls/RailStrip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ public RailStripKey(Geometry glyph, string pageName)

public bool IsActiveKey { get; private set; }

/// <summary>Pointer state for the housing lamp (D-088). These keys draw
/// themselves rather than living in a template, so the seam light that the
/// key styles get from XAML has to be built here instead. D-080 (4) put the
/// rail in scope precisely so the most-clicked surface in the app would not
/// be the one thing that stopped responding.</summary>
private bool _hot;
private bool _held;

public void SetActive(bool active)
{
IsActiveKey = active;
Expand Down Expand Up @@ -104,5 +112,46 @@ public void Restyle()
_glyph.Effect = null;
BoxShadow = BoxShadows.Parse(dark ? "0 1.5 3 0 #70000000" : "0 1.5 3 0 #30536057");
}

// the housing lamp under the key, over whatever shadow the state above
// chose. An active key keeps its teal halo and gains the warm one, the
// same way a lit key on a console still lights its own seam.
if (_hot || _held)
{
string seam = this.TryFindResource("KeySeamColor", ActualThemeVariant, out object? c) && c is Color sc
? $"{sc.R:X2}{sc.G:X2}{sc.B:X2}"
: (dark ? "F0A24A" : "C9762A");
string alpha = _held ? "B0" : "55";
BoxShadow = BoxShadows.Parse($"{BoxShadow}, 0 1 13 0 #{alpha}{seam}");
}
}

protected override void OnPointerEntered(Avalonia.Input.PointerEventArgs e)
{
base.OnPointerEntered(e);
_hot = true;
Restyle();
}

protected override void OnPointerExited(Avalonia.Input.PointerEventArgs e)
{
base.OnPointerExited(e);
_hot = false;
_held = false;
Restyle();
}

protected override void OnPointerPressed(Avalonia.Input.PointerPressedEventArgs e)
{
base.OnPointerPressed(e);
_held = true;
Restyle();
}

protected override void OnPointerReleased(Avalonia.Input.PointerReleasedEventArgs e)
{
base.OnPointerReleased(e);
_held = false;
Restyle();
}
}
79 changes: 58 additions & 21 deletions src/CUETools.Linux.App/Theme/AnalogControls.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -355,25 +355,48 @@
<Setter Property="Margin" Value="0,1,2,1" />
<Setter Property="Template">
<ControlTemplate>
<Border Name="bankKey"
Background="Transparent"
BorderBrush="Transparent"
BorderThickness="1"
CornerRadius="5"
Padding="{TemplateBinding Padding}">
<StackPanel Spacing="3">
<ContentPresenter Name="bankKeyContent"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
HorizontalAlignment="Center" />
<!-- the detent pip: the lamp that says which position is engaged -->
<Border Name="bankPip"
Height="3" Width="20" CornerRadius="1.5"
HorizontalAlignment="Center"
Opacity="0.55"
Background="{DynamicResource Line}" />
</StackPanel>
</Border>
<Panel ClipToBounds="False">
<!-- D-088, same fixture as a full key: the housing lamp under this
position, seen through its seam. Tighter and dimmer than a
button's, because bank keys sit 2 DIP apart and a button-sized
bloom on each would wash across its neighbours.
It deliberately does NOT light the selected position. The teal
pip says which one is engaged and the amber says which one you
are touching; running both off the same key would collapse two
different questions into one light. -->
<Border Name="bankSeam" Opacity="0" IsHitTestVisible="False"
ClipToBounds="False" CornerRadius="5">
<Border.Background>
<LinearGradientBrush StartPoint="0%,0%" EndPoint="0%,100%">
<GradientStop Color="{DynamicResource KeySeamColor}" Offset="0" />
<GradientStop Color="{DynamicResource KeySeamColor}" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<Border.Effect>
<DropShadowEffect OffsetX="0" OffsetY="1" BlurRadius="12" Opacity="0.9"
Color="{DynamicResource KeySeamColor}" />
</Border.Effect>
</Border>
<Border Name="bankKey"
Background="Transparent"
BorderBrush="Transparent"
BorderThickness="1"
CornerRadius="5"
Padding="{TemplateBinding Padding}">
<StackPanel Spacing="3">
<ContentPresenter Name="bankKeyContent"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
HorizontalAlignment="Center" />
<!-- the detent pip: the lamp that says which position is engaged -->
<Border Name="bankPip"
Height="3" Width="20" CornerRadius="1.5"
HorizontalAlignment="Center"
Opacity="0.55"
Background="{DynamicResource Line}" />
</StackPanel>
</Border>
</Panel>
</ControlTemplate>
</Setter>
</Style>
Expand All @@ -388,8 +411,22 @@
<Setter Property="FontSize" Value="11.5" />
</Style>

<Style Selector="ListBox.bank ListBoxItem:pointerover /template/ Border#bankKey">
<Setter Property="BorderBrush" Value="{DynamicResource Line}" />
<!-- light, not an outline: the same replacement D-088 made on the keys -->
<Style Selector="ListBox.bank ListBoxItem /template/ Border#bankSeam">
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.16" Easing="CubicEaseOut" />
</Transitions>
</Setter>
</Style>
<Style Selector="ListBox.bank ListBoxItem:pointerover /template/ Border#bankSeam">
<Setter Property="Opacity" Value="0.26" />
</Style>
<Style Selector="ListBox.bank ListBoxItem:pressed /template/ Border#bankSeam">
<Setter Property="Opacity" Value="0.7" />
</Style>
<Style Selector="ListBox.bank:disabled ListBoxItem /template/ Border#bankSeam">
<Setter Property="Opacity" Value="0" />
</Style>
<Style Selector="ListBox.bank ListBoxItem:pressed /template/ Border#bankKey">
<Setter Property="Background" Value="{DynamicResource ButtonPressed}" />
Expand Down
24 changes: 24 additions & 0 deletions tests/CUETools.Linux.Tests/SeamLightTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,30 @@ public void HoverNoLongerDrawsAnOutlineOnTheKeyFace()
Theme());
}

[Fact]
public void NoBankPositionOutlinesItselfOnHoverEither()
{
// the bank keys had the same outline, for the same reason, and it would
// have been easy to fix one surface and leave the other
Assert.DoesNotMatch(
new Regex(@"ListBoxItem:pointerover\s*/template/\s*Border#bankKey"),
Theme());
Assert.Matches(
new Regex(@"ListBoxItem:pointerover\s*/template/\s*Border#bankSeam"),
Theme());
}

[Fact]
public void TheBankLampNeverAnswersTheQuestionThePipAlreadyAnswers()
{
// two lights, two questions: the teal pip says which position is
// engaged, the warm lamp says which one the pointer is on. Lighting the
// seam of the SELECTED key would collapse them into one ambiguous glow.
Assert.DoesNotMatch(
new Regex(@"ListBoxItem:selected\s*/template/\s*Border#bankSeam"),
Theme());
}

[AvaloniaFact]
public void TheHousingLampIsWarmAndNotMistakableForTheAccent()
{
Expand Down
Loading