customize a VU meter just for us; refactor ChannelBox;

pull/1/head
Bryan Biedenkapp 11 months ago
parent 5eb294f273
commit ca554b874c

@ -1,13 +1,14 @@
<UserControl x:Class="dvmconsole.Controls.ChannelBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="220" Height="100">
xmlns:local="clr-namespace:dvmconsole.Controls"
Width="220" Height="110">
<UserControl.Effect>
<DropShadowEffect ShadowDepth="1" BlurRadius="16" Color="#FF686767" Opacity="0.3" />
</UserControl.Effect>
<!-- Border wraps the entire Grid -->
<Border x:Name="ControlBorder" BorderBrush="LightGray" BorderThickness="1,1,1,1" CornerRadius="8">
<Grid Margin="5">
<Grid Margin="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="41"/>
<ColumnDefinition/>
@ -21,23 +22,11 @@
<RowDefinition Height="7.75"/>
</Grid.RowDefinitions>
<!-- Left Side PTT Button -->
<!-- Main Info Section -->
<Rectangle Grid.ColumnSpan="2" HorizontalAlignment="Left" Height="40" Margin="1,0,0,0"
Grid.RowSpan="2" Grid.Row="2" VerticalAlignment="Top"
Width="116" StrokeThickness="0"
RadiusX="2" RadiusY="2">
<Rectangle.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFF0F0F0" Offset="0.485"/>
<GradientStop Color="Gainsboro" Offset="0.517"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<!-- Volume Meter -->
<local:VuMeterControl x:Name="VolumeMeter" HorizontalAlignment="Center" Width="219" Height="10" Grid.RowSpan="2" Grid.ColumnSpan="3" Margin="0,-58,0,0" />
<!-- Main Info Section -->
<StackPanel Grid.Column="1" HorizontalAlignment="Left" Width="119" Margin="48,0,0,1" Grid.RowSpan="2" Grid.ColumnSpan="2">
<StackPanel Grid.Column="1" HorizontalAlignment="Left" Width="147" Height="50" Margin="22,5,0,4" Grid.ColumnSpan="2" Grid.Row="1">
<TextBlock x:Name="ChannelTextBlock" FontWeight="Bold" Foreground="White" FontSize="12">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} ({1})">
@ -47,23 +36,11 @@
</TextBlock.Text>
</TextBlock>
<TextBlock Text="{Binding LastSrcId}" Foreground="Gold" FontSize="10"/>
<TextBlock Text="{Binding SystemName}" Foreground="Gold" FontSize="10"/>
<TextBlock Text="{Binding SystemName}" FontStyle="Italic" Foreground="Gold" FontSize="10"/>
</StackPanel>
<!-- Volume Meter -->
<ProgressBar x:Name="VolumeMeter" HorizontalAlignment="Center" Width="16" Margin="0,1,0,5" Grid.RowSpan="2" Orientation="Vertical" Maximum="50"
BorderThickness="0" Background="#FFF0F0F0">
<ProgressBar.Foreground>
<LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
<GradientStop Color="#F0C60000" Offset="1"/>
<GradientStop Color="#F0C68700" Offset="0.9"/>
<GradientStop Color="#F0008E00" Offset="0"/>
</LinearGradientBrush>
</ProgressBar.Foreground>
</ProgressBar>
<!-- PTT Button -->
<Button x:Name="PttButton" HorizontalAlignment="Left" Width="42" Margin="41,1,0,4" Grid.ColumnSpan="2" BorderThickness="0,0,0,0" BorderBrush="#FFC1C1C1" UseLayoutRounding="False" Grid.RowSpan="2" Background="#FFEEA400"
<Button x:Name="PttButton" HorizontalAlignment="Left" Width="42" Height="42" Margin="12,5,0,4" Grid.ColumnSpan="2" BorderThickness="0,0,0,0" BorderBrush="#FFC1C1C1" UseLayoutRounding="False" Grid.RowSpan="2" Background="#FFEEA400"
ToolTip="Push To Talk">
<Button.Resources>
<Style TargetType="{x:Type Border}">
@ -73,10 +50,22 @@
<Image Source="/dvmconsole;component/Assets/instantptt.png" Width="39" Height="40" Stretch="Fill" Margin="0,0,0,1"/>
</Button>
<!-- Volume Slider Background -->
<Rectangle Grid.ColumnSpan="2" HorizontalAlignment="Left" Height="40" Margin="6,-4,0,0"
Grid.RowSpan="2" Grid.Row="2" VerticalAlignment="Top" Width="116" StrokeThickness="0"
RadiusX="4" RadiusY="4">
<Rectangle.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFF0F0F0" Offset="0.485"/>
<GradientStop Color="Gainsboro" Offset="0.517"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<!-- Volume Slider -->
<Slider Minimum="0" Maximum="4" Value="{Binding Volume, Mode=TwoWay}"
Height="21" VerticalAlignment="Top" x:Name="VolumeSlider"
ValueChanged="VolumeSlider_ValueChanged" Margin="11,10,65,0"
Height="21" VerticalAlignment="Center" x:Name="VolumeSlider"
ValueChanged="VolumeSlider_ValueChanged" Margin="12,0,72,0"
Grid.ColumnSpan="2" Grid.Row="2" ToolTip="Channel Volume">
<Slider.Style>
<Style TargetType="Slider">
@ -111,7 +100,7 @@
</Slider>
<!-- Bottom Buttons -->
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Margin="81,0,-2,0" Grid.RowSpan="2" Grid.ColumnSpan="2">
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Margin="87,-8,-2,0" Grid.RowSpan="2" Grid.ColumnSpan="2">
<Button Width="40" Height="40" x:Name="PageSelectButton" Click="PageSelectButton_Click" BorderThickness="0,0,0,0"
ToolTip="Channel Alert">
<Button.Resources>

@ -26,7 +26,7 @@ using fnecore.P25;
namespace dvmconsole.Controls
{
/// <summary>
///
/// Interaction logic for ChannelBox.xaml.
/// </summary>
public partial class ChannelBox : UserControl, INotifyPropertyChanged
{
@ -86,6 +86,8 @@ namespace dvmconsole.Controls
private bool pttToggleMode = false;
private bool isPrimary = false;
/*
** Properties
*/
@ -113,7 +115,11 @@ namespace dvmconsole.Controls
public new Brush Background
{
get => ControlBorder.Background;
set => ControlBorder.Background = value;
set
{
ControlBorder.Background = value;
SetVolumeMeterBg(value);
}
}
/*
@ -268,14 +274,19 @@ namespace dvmconsole.Controls
}
}
private bool isPrimary = false;
/// <summary>
///
/// </summary>
public bool IsPrimary
{
get => isPrimary;
set
{
isPrimary = value;
UpdateBackground();
Dispatcher.Invoke(() =>
{
UpdateBackground();
});
}
}
@ -306,7 +317,7 @@ namespace dvmconsole.Controls
OnPropertyChanged(nameof(VolumeMeterLevel));
Dispatcher.Invoke(() =>
{
VolumeMeter.Value = 100 * value;
VolumeMeter.ViewModel.Level = value;
});
}
}
@ -340,8 +351,8 @@ namespace dvmconsole.Controls
EndPoint = new Point(0.5, 1)
};
DARK_GRAY_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0979797"), 0.485));
DARK_GRAY_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0686767"), 0.517));
DARK_GRAY_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0979797"), 0.535));
DARK_GRAY_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0686767"), 0.567));
BLUE_GRADIENT = new LinearGradientBrush
{
@ -349,8 +360,8 @@ namespace dvmconsole.Controls
EndPoint = new Point(0.5, 1)
};
BLUE_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0150189"), 0.485));
BLUE_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F00B004B"), 0.517));
BLUE_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0150189"), 0.535));
BLUE_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F00B004B"), 0.567));
RED_GRADIENT = new LinearGradientBrush
{
@ -358,8 +369,8 @@ namespace dvmconsole.Controls
EndPoint = new Point(0.5, 1)
};
RED_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0FF0000"), 0.485));
RED_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0C60000"), 0.517));
RED_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0FF0000"), 0.535));
RED_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0C60000"), 0.567));
GREEN_GRADIENT = new LinearGradientBrush
{
@ -367,8 +378,8 @@ namespace dvmconsole.Controls
EndPoint = new Point(0.5, 1)
};
GREEN_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F000AF00"), 0.485));
GREEN_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0008E00"), 0.517));
GREEN_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F000AF00"), 0.535));
GREEN_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0008E00"), 0.567));
ORANGE_GRADIENT = new LinearGradientBrush
{
@ -376,8 +387,8 @@ namespace dvmconsole.Controls
EndPoint = new Point(0.5, 1)
};
ORANGE_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0FFAF00"), 0.485));
ORANGE_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0C68700"), 0.517));
ORANGE_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0FFAF00"), 0.535));
ORANGE_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0C68700"), 0.567));
BORDER_DEFAULT = new Border
{
BorderBrush = new SolidColorBrush(Colors.LightGray),
@ -419,6 +430,9 @@ namespace dvmconsole.Controls
SystemName = $"System: {systemName}";
LastSrcId = $"Last ID: {LastSrcId}";
VolumeMeter.ViewModel = new VuMeterViewModel();
VolumeMeter.ViewModel.Level = 0;
UpdateBackground();
MouseLeftButtonDown += ChannelBox_MouseLeftButtonDown;
@ -544,6 +558,23 @@ namespace dvmconsole.Controls
ControlBorder.BorderBrush = BORDER_DEFAULT.BorderBrush;
else
ControlBorder.BorderBrush = BORDER_DEFAULT.BorderBrush;
SetVolumeMeterBg(ControlBorder.Background);
}
/// <summary>
///
/// </summary>
/// <param name="bg"></param>
private void SetVolumeMeterBg(Brush bg)
{
if (bg is LinearGradientBrush)
{
LinearGradientBrush gradient = bg as LinearGradientBrush;
VolumeMeter.SetBackground(new SolidColorBrush(gradient.GradientStops[0].Color));
}
else
VolumeMeter.SetBackground(bg);
}
/// <summary>

@ -0,0 +1,43 @@
<UserControl x:Class="dvmconsole.Controls.VuMeterControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:dvmconsole.Controls" d:DataContext="{d:DesignInstance Type=local:VuMeterViewModel}"
mc:Ignorable="d" d:DesignHeight="16" d:DesignWidth="200">
<Border x:Name="ControlBorder" BorderBrush="#FF979797" BorderThickness="1,1,1,1" CornerRadius="6,6,0,0">
<Grid x:Name="Container">
<Grid.Resources>
<LinearGradientBrush x:Key="VuMeterBrush">
<LinearGradientBrush.GradientStops>
<GradientStop Color="Green" Offset="0"/>
<GradientStop Color="LightGreen" Offset="0.1"/>
<GradientStop Color="Yellow" Offset="0.60"/>
<GradientStop Color="Orange" Offset="0.7"/>
<GradientStop Color="Red" Offset="0.9"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border x:Name="backgroundRect" BorderThickness="1" Grid.Column="2" Grid.ZIndex="1" Height="14" HorizontalAlignment="Stretch"
CornerRadius="5,5,0,0" BorderBrush="#FF979797" Background="#FF979797">
</Border>
<Border BorderThickness="1" Grid.Column="2" Grid.ZIndex="2" Height="6" HorizontalAlignment="Stretch" Margin="0.15,0,0.15,0"
CornerRadius="4,4,0,0" BorderBrush="{StaticResource VuMeterBrush}" Background="{StaticResource VuMeterBrush}">
</Border>
<Rectangle x:Name="maskRect" Grid.Column="2" Grid.ZIndex="3" Height="8" HorizontalAlignment="Right" Margin="0,1,0,0" Fill="#FF979797">
<Rectangle.Width>
<MultiBinding Converter="{x:Static local:SizePercentConverter.Instance}">
<Binding ElementName="Container" Path="ActualWidth" FallbackValue="100"/>
<Binding Path="InvertedLevel" FallbackValue="1"/>
</MultiBinding>
</Rectangle.Width>
</Rectangle>
</Grid>
</Border>
</UserControl>

@ -0,0 +1,192 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
*
*/
using System.ComponentModel;
using System.Globalization;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Shapes;
namespace dvmconsole.Controls
{
/// <summary>
/// Convert a size to a double according a percent ratio (0 to 1).
/// </summary>
public class SizePercentConverter : IMultiValueConverter
{
private static SizePercentConverter instance;
/*
** Properties
*/
/// <summary>
///
/// </summary>
public static SizePercentConverter Instance => instance ?? (instance = new SizePercentConverter());
/*
** Methods
*/
/// <summary>
/// Convert a size to a double according a percent ratio (0 to 1).
/// </summary>
/// <param name="values"></param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns></returns>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
=> Math.Max(0, System.Convert.ToDouble(values[0]) * System.Convert.ToDouble(values[1]));
/// <summary>
///
/// </summary>
/// <param name="value"></param>
/// <param name="targetTypes"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
} // public class SizePercentConverter : IMultiValueConverter
/// <summary>
/// View model for the VU meter.
/// </summary>
public class VuMeterViewModel : INotifyPropertyChanged
{
public double LevelScaleFactor { get; set; } = 1d / 20000d;
protected double level = 0;
protected double invertedLevel = 1;
/*
** Properties
*/
/// <summary>
/// Audio Level.
/// </summary>
public double Level
{
get => level;
set
{
level = value;
OnPropertyChanged("Level");
InvertedLevel = 1 - level;
}
}
/// <summary>
/// Inverted audio level.
/// </summary>
public double InvertedLevel
{
get => invertedLevel;
set
{
invertedLevel = value;
OnPropertyChanged("InvertedLevel");
}
}
/*
** Events
*/
/// <summary>
/// Event action that occurs when a property changes on this control.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/*
** Methods
*/
/// <summary>
/// Initializes a new instance of the <see cref="VuMeterViewModel"/> class.
/// </summary>
public VuMeterViewModel()
{
/* stub */
}
/// <summary>
///
/// </summary>
public void Reset() => Level = 0;
/// <summary>
///
/// </summary>
/// <param name="propertyName"></param>
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
} // public class VuMeterViewModel : INotifyPropertyChanged
/// <summary>
/// Interaction logic for VuMeterControl.xaml.
/// </summary>
public partial class VuMeterControl : UserControl
{
private VuMeterViewModel viewModel;
private Brush borderColor;
private Shape fillColor;
/// <summary>
///
/// </summary>
public VuMeterViewModel ViewModel
{
get => viewModel;
set
{
viewModel = value;
DataContext = viewModel;
}
}
/*
** Methods
*/
/// <summary>
/// Initializes a new instance of the <see cref="VuMeterControl"/> class.
/// </summary>
public VuMeterControl()
{
InitializeComponent();
}
/// <summary>
///
/// </summary>
/// <param name="brush"></param>
public void SetBackground(Brush brush)
{
ControlBorder.BorderBrush = brush;
backgroundRect.BorderBrush = brush;
backgroundRect.Background = brush;
maskRect.Fill = brush;
}
} // public partial class VuMeterControl : UserControl
} // namespace dvmconsole.Controls
Loading…
Cancel
Save

Powered by TurnKey Linux.