Add brightness/contrast sliders, input source switching, and 9-language localization
- Add VCP commands for brightness (10), contrast (12), input source (60) - Fix UTF-16 encoding for monitor data parsing - Add system tray app with monitor controls - Add localization for en, es, fr, de, zh, ja, pt, it, hi - Update to .NET 9.0 - Add LICENSE and README 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,19 +1,20 @@
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows;
|
||||
using CMM.Library.ViewModel;
|
||||
using CMM.Library.Base;
|
||||
using CMM.Library.Method;
|
||||
using System.Windows.Data;
|
||||
using CMM.Language;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CMM.Management.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// 單一顆螢幕
|
||||
/// Single monitor control
|
||||
/// </summary>
|
||||
internal class MonCtrl : System.Windows.Controls.Control
|
||||
{
|
||||
public readonly static DependencyProperty MonProperty;
|
||||
public static readonly DependencyProperty MonProperty;
|
||||
private StackPanel _sp;
|
||||
|
||||
static MonCtrl()
|
||||
@@ -30,6 +31,10 @@ namespace CMM.Management.Control
|
||||
public override void OnApplyTemplate()
|
||||
{
|
||||
_sp = Template.FindName("sp", this) as StackPanel;
|
||||
if (_sp != null && Mon != null)
|
||||
{
|
||||
_ = LoadControlsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public XMonitor Mon
|
||||
@@ -49,7 +54,236 @@ namespace CMM.Management.Control
|
||||
|
||||
public virtual void OnMonChanged(XMonitor value)
|
||||
{
|
||||
|
||||
if (_sp != null && value != null)
|
||||
{
|
||||
_ = LoadControlsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadControlsAsync()
|
||||
{
|
||||
if (_sp == null || Mon == null) return;
|
||||
|
||||
_sp.Children.Clear();
|
||||
|
||||
var brightness = await CMMCommand.GetBrightness(Mon.SerialNumber);
|
||||
var contrast = await CMMCommand.GetContrast(Mon.SerialNumber);
|
||||
var inputSource = await CMMCommand.GetInputSource(Mon.SerialNumber);
|
||||
var inputOptions = await CMMCommand.GetInputSourceOptions(Mon.SerialNumber);
|
||||
var powerStatus = await CMMCommand.GetMonPowerStatus(Mon.SerialNumber);
|
||||
|
||||
// Brightness slider
|
||||
var brightnessPanel = CreateSliderControl(
|
||||
Lang.Find("Brightness"),
|
||||
brightness ?? 50,
|
||||
Mon.SerialNumber,
|
||||
async (sn, value) => await CMMCommand.SetBrightness(sn, value));
|
||||
_sp.Children.Add(brightnessPanel);
|
||||
|
||||
// Contrast slider
|
||||
var contrastPanel = CreateSliderControl(
|
||||
Lang.Find("Contrast"),
|
||||
contrast ?? 50,
|
||||
Mon.SerialNumber,
|
||||
async (sn, value) => await CMMCommand.SetContrast(sn, value));
|
||||
_sp.Children.Add(contrastPanel);
|
||||
|
||||
// Input source dropdown
|
||||
if (inputOptions.Count > 0)
|
||||
{
|
||||
var inputPanel = CreateInputSourceControl(
|
||||
Mon.SerialNumber,
|
||||
inputSource,
|
||||
inputOptions);
|
||||
_sp.Children.Add(inputPanel);
|
||||
}
|
||||
|
||||
// Power button
|
||||
var powerPanel = CreatePowerControl(Mon.SerialNumber, powerStatus);
|
||||
_sp.Children.Add(powerPanel);
|
||||
}
|
||||
|
||||
private StackPanel CreateSliderControl(
|
||||
string label,
|
||||
int currentValue,
|
||||
string monitorSN,
|
||||
Func<string, int, Task> onValueChanged)
|
||||
{
|
||||
var panel = new StackPanel
|
||||
{
|
||||
Orientation = Orientation.Horizontal,
|
||||
Margin = new Thickness(0, 5, 0, 5)
|
||||
};
|
||||
|
||||
var labelBlock = new TextBlock
|
||||
{
|
||||
Text = label,
|
||||
Width = 100,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Foreground = System.Windows.Media.Brushes.White
|
||||
};
|
||||
|
||||
var slider = new Slider
|
||||
{
|
||||
Style = (Style)FindResource("Horizontal_Slider"),
|
||||
Minimum = 0,
|
||||
Maximum = 100,
|
||||
Value = currentValue,
|
||||
Tag = monitorSN,
|
||||
Width = 200
|
||||
};
|
||||
|
||||
var valueBlock = new TextBlock
|
||||
{
|
||||
Text = currentValue.ToString(),
|
||||
Width = 40,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Foreground = System.Windows.Media.Brushes.White,
|
||||
TextAlignment = TextAlignment.Right
|
||||
};
|
||||
|
||||
// Update value display while dragging
|
||||
slider.ValueChanged += (s, e) =>
|
||||
{
|
||||
valueBlock.Text = ((int)e.NewValue).ToString();
|
||||
};
|
||||
|
||||
// Only send command on release (PreviewMouseUp)
|
||||
slider.PreviewMouseUp += async (s, e) =>
|
||||
{
|
||||
var sld = s as Slider;
|
||||
var sn = sld?.Tag?.ToString();
|
||||
if (!string.IsNullOrEmpty(sn))
|
||||
{
|
||||
await onValueChanged(sn, (int)sld.Value);
|
||||
}
|
||||
};
|
||||
|
||||
panel.Children.Add(labelBlock);
|
||||
panel.Children.Add(slider);
|
||||
panel.Children.Add(valueBlock);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private StackPanel CreateInputSourceControl(
|
||||
string monitorSN,
|
||||
int? currentInputSource,
|
||||
List<InputSourceOption> options)
|
||||
{
|
||||
var panel = new StackPanel
|
||||
{
|
||||
Orientation = Orientation.Horizontal,
|
||||
Margin = new Thickness(0, 5, 0, 5)
|
||||
};
|
||||
|
||||
var labelBlock = new TextBlock
|
||||
{
|
||||
Text = Lang.Find("InputSource"),
|
||||
Width = 100,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Foreground = System.Windows.Media.Brushes.White
|
||||
};
|
||||
|
||||
var comboBox = new ComboBox
|
||||
{
|
||||
Width = 200,
|
||||
Tag = monitorSN,
|
||||
ItemsSource = options,
|
||||
DisplayMemberPath = "Name"
|
||||
};
|
||||
|
||||
// Set current selection
|
||||
if (currentInputSource.HasValue)
|
||||
{
|
||||
var currentOption = options.Find(o => o.Value == currentInputSource.Value);
|
||||
if (currentOption != null)
|
||||
{
|
||||
comboBox.SelectedItem = currentOption;
|
||||
}
|
||||
}
|
||||
|
||||
comboBox.SelectionChanged += async (s, e) =>
|
||||
{
|
||||
var cb = s as ComboBox;
|
||||
var sn = cb?.Tag?.ToString();
|
||||
var selectedOption = cb?.SelectedItem as InputSourceOption;
|
||||
|
||||
if (!string.IsNullOrEmpty(sn) && selectedOption != null)
|
||||
{
|
||||
await CMMCommand.SetInputSource(sn, selectedOption.Value);
|
||||
}
|
||||
};
|
||||
|
||||
panel.Children.Add(labelBlock);
|
||||
panel.Children.Add(comboBox);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private StackPanel CreatePowerControl(string monitorSN, string powerStatus)
|
||||
{
|
||||
var panel = new StackPanel
|
||||
{
|
||||
Orientation = Orientation.Horizontal,
|
||||
Margin = new Thickness(0, 10, 0, 0)
|
||||
};
|
||||
|
||||
var labelBlock = new TextBlock
|
||||
{
|
||||
Text = Lang.Find("Power"),
|
||||
Width = 100,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Foreground = System.Windows.Media.Brushes.White
|
||||
};
|
||||
|
||||
var btn = new Button
|
||||
{
|
||||
Tag = monitorSN,
|
||||
Content = GetLocalizedPowerStatus(powerStatus),
|
||||
Width = 200,
|
||||
Height = 30
|
||||
};
|
||||
|
||||
btn.Click += async (s, e) => await TogglePower(s, e);
|
||||
|
||||
panel.Children.Add(labelBlock);
|
||||
panel.Children.Add(btn);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private string GetLocalizedPowerStatus(string status)
|
||||
{
|
||||
return status switch
|
||||
{
|
||||
"PowerOn" => Lang.Find("PowerOn"),
|
||||
"Sleep" => Lang.Find("Sleep"),
|
||||
"PowerOff" => Lang.Find("PowerOff"),
|
||||
_ => status
|
||||
};
|
||||
}
|
||||
|
||||
private async Task TogglePower(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var btn = sender as Button;
|
||||
var tag = btn?.Tag?.ToString();
|
||||
if (string.IsNullOrEmpty(tag)) return;
|
||||
|
||||
var currentStatus = await CMMCommand.GetMonPowerStatus(tag);
|
||||
|
||||
if (currentStatus == "Sleep" || currentStatus == "PowerOff")
|
||||
{
|
||||
await CMMCommand.PowerOn(tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
await CMMCommand.Sleep(tag);
|
||||
}
|
||||
|
||||
await Task.Delay(1000);
|
||||
var newStatus = await CMMCommand.GetMonPowerStatus(tag);
|
||||
btn!.Content = GetLocalizedPowerStatus(newStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user