(GUI): added settings dialog.

This commit is contained in:
morkt 2018-01-10 02:44:21 +04:00
parent 6af3e14f34
commit 13829ca798
5 changed files with 466 additions and 0 deletions

View File

@ -176,6 +176,9 @@
<SubType>Component</SubType> <SubType>Component</SubType>
</Compile> </Compile>
<Compile Include="Settings.cs" /> <Compile Include="Settings.cs" />
<Compile Include="SettingsWindow.xaml.cs">
<DependentUpon>SettingsWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Shell.cs" /> <Compile Include="Shell.cs" />
<Compile Include="Strings\guiStrings.Designer.cs"> <Compile Include="Strings\guiStrings.Designer.cs">
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
@ -238,6 +241,10 @@
<DependentUpon>MainWindow.xaml</DependentUpon> <DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Page Include="SettingsWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="TextViewer.xaml"> <Page Include="TextViewer.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>

View File

@ -135,6 +135,9 @@
<MenuItem Header="{x:Static s:guiStrings.MenuExit}" Command="{x:Static local:Commands.Exit}" InputGestureText="Ctrl+Q"/> <MenuItem Header="{x:Static s:guiStrings.MenuExit}" Command="{x:Static local:Commands.Exit}" InputGestureText="Ctrl+Q"/>
</MenuItem> </MenuItem>
<MenuItem Header="{x:Static s:guiStrings.MenuView}"> <MenuItem Header="{x:Static s:guiStrings.MenuView}">
<MenuItem Header="Preferences" InputGestureText="Ctrl+P"
Command="{x:Static local:Commands.Preferences}"/>
<Separator/>
<MenuItem Header="{x:Static s:guiStrings.MenuFitWindow}" InputGestureText="Ctrl+H" <MenuItem Header="{x:Static s:guiStrings.MenuFitWindow}" InputGestureText="Ctrl+H"
Command="{x:Static local:Commands.FitWindow}"/> Command="{x:Static local:Commands.FitWindow}"/>
<MenuItem Header="{x:Static s:guiStrings.CtxMenuRefresh}" InputGestureText="F5" <MenuItem Header="{x:Static s:guiStrings.CtxMenuRefresh}" InputGestureText="F5"
@ -372,6 +375,7 @@
<KeyBinding Gesture="Ctrl+Q" Command="{x:Static local:Commands.Exit}"/> <KeyBinding Gesture="Ctrl+Q" Command="{x:Static local:Commands.Exit}"/>
<KeyBinding Gesture="Ctrl+H" Command="{x:Static local:Commands.FitWindow}"/> <KeyBinding Gesture="Ctrl+H" Command="{x:Static local:Commands.FitWindow}"/>
<KeyBinding Gesture="Ctrl+A" Command="{x:Static local:Commands.SelectAll}"/> <KeyBinding Gesture="Ctrl+A" Command="{x:Static local:Commands.SelectAll}"/>
<KeyBinding Gesture="Ctrl+P" Command="{x:Static local:Commands.Preferences}"/>
<KeyBinding Gesture="Backspace" Command="{x:Static local:Commands.GoBack}"/> <KeyBinding Gesture="Backspace" Command="{x:Static local:Commands.GoBack}"/>
<KeyBinding Gesture="Alt+Left" Command="{x:Static local:Commands.GoBack}"/> <KeyBinding Gesture="Alt+Left" Command="{x:Static local:Commands.GoBack}"/>
<KeyBinding Gesture="Alt+Right" Command="{x:Static local:Commands.GoForward}"/> <KeyBinding Gesture="Alt+Right" Command="{x:Static local:Commands.GoForward}"/>
@ -412,6 +416,7 @@
<CommandBinding Command="{x:Static local:Commands.About}" Executed="AboutExec" CanExecute="CanExecuteAlways"/> <CommandBinding Command="{x:Static local:Commands.About}" Executed="AboutExec" CanExecute="CanExecuteAlways"/>
<CommandBinding Command="{x:Static local:Commands.CheckUpdates}" Executed="CheckUpdatesExec" CanExecute="CanExecuteUpdate"/> <CommandBinding Command="{x:Static local:Commands.CheckUpdates}" Executed="CheckUpdatesExec" CanExecute="CanExecuteUpdate"/>
<CommandBinding Command="{x:Static local:Commands.StopPlayback}" Executed="StopPlaybackExec" CanExecute="CanExecutePlaybackControl"/> <CommandBinding Command="{x:Static local:Commands.StopPlayback}" Executed="StopPlaybackExec" CanExecute="CanExecutePlaybackControl"/>
<CommandBinding Command="{x:Static local:Commands.Preferences}" Executed="PreferencesExec" CanExecute="CanExecuteAlways"/>
<CommandBinding Command="{x:Static local:Commands.Exit}" Executed="ExitExec" CanExecute="CanExecuteAlways"/> <CommandBinding Command="{x:Static local:Commands.Exit}" Executed="ExitExec" CanExecute="CanExecuteAlways"/>
</Window.CommandBindings> </Window.CommandBindings>
</Window> </Window>

View File

@ -1289,6 +1289,13 @@ namespace GARbro.GUI
about.ShowDialog(); about.ShowDialog();
} }
private void PreferencesExec (object sender, ExecutedRoutedEventArgs e)
{
var settings = new SettingsWindow();
settings.Owner = this;
settings.ShowDialog();
}
private void CanExecuteAlways (object sender, CanExecuteRoutedEventArgs e) private void CanExecuteAlways (object sender, CanExecuteRoutedEventArgs e)
{ {
e.CanExecute = true; e.CanExecute = true;
@ -1517,5 +1524,6 @@ namespace GARbro.GUI
public static readonly RoutedCommand NextItem = new RoutedCommand(); public static readonly RoutedCommand NextItem = new RoutedCommand();
public static readonly RoutedCommand CopyNames = new RoutedCommand(); public static readonly RoutedCommand CopyNames = new RoutedCommand();
public static readonly RoutedCommand StopPlayback = new RoutedCommand(); public static readonly RoutedCommand StopPlayback = new RoutedCommand();
public static readonly RoutedCommand Preferences = new RoutedCommand();
} }
} }

178
GUI/SettingsWindow.xaml Normal file
View File

@ -0,0 +1,178 @@
<!-- Game Resource browser
Copyright (C) 2018 by morkt
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
-->
<Window x:Class="GARbro.GUI.SettingsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:GARbro.GUI.Strings"
xmlns:local="clr-namespace:GARbro.GUI"
Title="Settings" ShowInTaskbar="False" ResizeMode="NoResize"
FocusManager.FocusedElement="{Binding ElementName=SectionsPane}"
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Height="400" Width="400" WindowStartupLocation="CenterOwner">
<Window.Resources>
<LinearGradientBrush x:Key="SectionTitleBackground" StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="#FFA0A0FF" Offset="0"/>
<GradientStop Color="#FFE0E0FF" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ItemSelectedBackground" StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="#FF6AA2DC" Offset="0"/>
<GradientStop Color="#FF226EBC" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ItemInactiveBackground" StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="#FFA4B0DB" Offset="0"/>
<GradientStop Color="#FF91A2C4" Offset="1"/>
</LinearGradientBrush>
<ControlTemplate x:Key="BoundCheckBox" TargetType="CheckBox">
<CheckBox DataContext="{TemplateBinding DataContext}" IsChecked="{Binding Value}"
Content="{Binding Text}" ToolTip="{Binding Description}" Margin="2"/>
</ControlTemplate>
<Style x:Key="ExpandCollapseToggleStyle" TargetType="ToggleButton">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border Padding="5" Background="#00FFFFFF" Width="16" Height="16">
<Path Fill="#FF595959" Stroke="#FF262626" x:Name="ExpandPath">
<Path.Data>
<PathGeometry Figures="M0,0L0,6L6,0z" />
</Path.Data>
<Path.RenderTransform>
<RotateTransform Angle="135" CenterX="3" CenterY="3" />
</Path.RenderTransform>
</Path>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ExpandPath" Property="Stroke" Value="#FF1BBBFF" />
<Setter TargetName="ExpandPath" Property="Fill" Value="#FFFFFFFF" />
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="ExpandPath" Property="RenderTransform">
<Setter.Value>
<RotateTransform Angle="180" CenterX="3" CenterY="3" />
</Setter.Value>
</Setter>
<Setter TargetName="ExpandPath" Property="Fill" Value="#FF595959" />
<Setter TargetName="ExpandPath" Property="Stroke" Value="#FF262626" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="{x:Type TreeViewItem}" TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
<Setter Property="IsExpanded" Value="True"/>
<Setter Property="HorizontalContentAlignment" Value="Left" />
<!-- Force right-click to select TreeViewItem -->
<EventSetter Event="MouseRightButtonDown" Handler="tvi_MouseRightButtonDown" />
<!-- Select the whole TreeViewItem row, not only Header region -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<ControlTemplate.Resources>
<local:LeftMarginMultiplierConverter Length="19" x:Key="lengthConverter" />
</ControlTemplate.Resources>
<StackPanel>
<Border x:Name="Bd" HorizontalAlignment="Stretch" Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}" Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="True" Grid.Column="1">
<Grid Margin="{Binding Converter={StaticResource lengthConverter}, RelativeSource={RelativeSource TemplatedParent}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="19" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ToggleButton IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
Style="{StaticResource ExpandCollapseToggleStyle}"
ClickMode="Press" Name="Expander"/>
<ContentPresenter x:Name="PART_Header" Grid.Column="1" ContentSource="Header"
Content="{TemplateBinding HeaderedContentControl.Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
ContentStringFormat="{TemplateBinding HeaderStringFormat}"
ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Grid>
</Border>
<ItemsPresenter x:Name="ItemsHost"/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="False">
<Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed" />
</Trigger>
<Trigger Property="HasItems" Value="False">
<Setter TargetName="Expander" Property="Visibility" Value="Hidden" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Bd" Property="Background" Value="{StaticResource ItemSelectedBackground}"/>
<Setter Property="Foreground" Value="White" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True" />
<Condition Property="IsSelectionActive" Value="False" />
<Condition Property="IsFocused" Value="False" />
</MultiTrigger.Conditions>
<Setter TargetName="Bd" Property="Background" Value="{StaticResource ItemInactiveBackground}" />
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
</MultiTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<DockPanel Margin="8">
<StackPanel DockPanel.Dock="Bottom" Margin="0,10,0,0" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Bottom">
<Button Width="75" Height="25" Margin="0,0,10,0" Content="{x:Static s:guiStrings.ButtonOK}" IsDefault="True" Click="Button_ClickOk"/>
<Button Width="75" Height="25" Margin="0,0,10,0" Content="{x:Static s:guiStrings.ButtonCancel}" IsCancel="True"/>
<Button Width="75" Height="25" Margin="0" Content="Apply" Click="Button_ClickApply" IsEnabled="{Binding HasChanges}"/>
</StackPanel>
<TreeView x:Name="SectionsPane" DockPanel.Dock="Left" Padding="4" ItemsSource="{Binding Root}"
TreeViewItem.Selected="OnSectionChanged">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Label}" Padding="0,0,4,0"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<Border DockPanel.Dock="Right" BorderThickness="1" BorderBrush="Black" Margin="4,0,0,0" SnapsToDevicePixels="True">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Padding="3"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<StackPanel Orientation="Vertical" DataContext="{Binding ElementName=SectionsPane,Path=SelectedItem}">
<TextBlock Text="{Binding SectionTitle}" Background="{StaticResource SectionTitleBackground}"
FontWeight="Bold" FontSize="14" Padding="2,0,2,0"/>
<StackPanel x:Name="SettingsPane"/>
</StackPanel>
</ScrollViewer>
</Border>
</DockPanel>
</Window>

268
GUI/SettingsWindow.xaml.cs Normal file
View File

@ -0,0 +1,268 @@
/// Game Resource browser
//
// Copyright (C) 2018 by morkt
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using GameRes;
namespace GARbro.GUI
{
/// <summary>
/// Interaction logic for SettingsWindow.xaml
/// </summary>
public partial class SettingsWindow : Window
{
public SettingsWindow ()
{
InitializeComponent();
this.DataContext = this.ViewModel = CreateSettingsTree();
}
SettingsViewModel ViewModel;
private void OnSectionChanged (object sender, System.Windows.RoutedEventArgs e)
{
this.SettingsPane.Children.Clear();
var section = SectionsPane.SelectedValue as SettingsSectionView;
if (section != null && section.Panel != null)
this.SettingsPane.Children.Add (section.Panel);
}
private void Button_ClickApply (object sender, System.Windows.RoutedEventArgs e)
{
ApplyChanges();
}
private void Button_ClickOk (object sender, System.Windows.RoutedEventArgs e)
{
ApplyChanges();
DialogResult = true;
}
private void ApplyChanges ()
{
if (!ViewModel.HasChanges)
return;
if (OnApplyChanges != null)
OnApplyChanges (this, new EventArgs());
ViewModel.HasChanges = false;
}
private SettingsViewModel CreateSettingsTree ()
{
SettingsSectionView[] list = {
new SettingsSectionView {
Label = "Formats",
Children = EnumerateFormatsSettings()
},
};
list[0].IsSelected = true;
return new SettingsViewModel { Root = list };
}
IEnumerable<SettingsSectionView> EnumerateFormatsSettings ()
{
var default_margin = new Thickness (2);
var list = new List<SettingsSectionView>();
foreach (var format in FormatCatalog.Instance.Formats.Where (f => f.Settings != null && f.Settings.Any()))
{
var pane = new WrapPanel();
foreach (var setting in format.Settings)
{
if (setting.Value is bool)
{
var view = new ResourceSettingView<bool> (setting);
view.ValueChanged += (s, e) => ViewModel.HasChanges = true;
this.OnApplyChanges += (s, e) => view.Apply();
var check_box = new CheckBox {
Template = (ControlTemplate)this.Resources["BoundCheckBox"],
DataContext = view,
};
pane.Children.Add (check_box);
}
}
if (pane.Children.Count > 0)
{
var section = new SettingsSectionView {
Label = format.Tag,
SectionTitle = "Formats :: "+format.Tag,
Panel = pane
};
list.Add (section);
}
}
return list;
}
private void tvi_MouseRightButtonDown (object sender, MouseButtonEventArgs e)
{
var item = sender as TreeViewItem;
if (item != null && e.RightButton == MouseButtonState.Pressed)
{
item.Focus();
item.IsSelected = true;
e.Handled = true;
}
}
public delegate void ApplyEventHandler (object sender, EventArgs e);
public event ApplyEventHandler OnApplyChanges;
}
public class SettingsViewModel : INotifyPropertyChanged
{
public IEnumerable<SettingsSectionView> Root { get; set; }
bool m_has_changes;
public bool HasChanges {
get { return m_has_changes; }
set {
if (value != m_has_changes)
{
m_has_changes = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged ([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged (this, new PropertyChangedEventArgs (propertyName));
}
}
}
public class SettingsSectionView
{
public string Label { get; set; }
public bool IsSelected { get; set; }
public UIElement Panel { get; set; }
string m_title;
public string SectionTitle {
get { return m_title ?? Label; }
set { m_title = value; }
}
public IEnumerable<SettingsSectionView> Children { get; set; }
}
public class ResourceSettingView<TValue>
{
public IResourceSetting Source { get; private set; }
public bool IsChanged { get; private set; }
public string Text { get { return Source.Text; } }
public string Description { get { return Source.Description; } }
TValue m_value;
public TValue Value {
get { return m_value; }
set {
if (!EqualityComparer<TValue>.Default.Equals (m_value, value))
{
m_value = value;
IsChanged = true;
OnValueChanged();
}
}
}
public ResourceSettingView (IResourceSetting src)
{
Source = src;
m_value = (TValue)src.Value;
}
public void Apply ()
{
if (IsChanged)
{
Source.Value = m_value;
IsChanged = false;
}
}
public event PropertyChangedEventHandler ValueChanged;
void OnValueChanged ()
{
if (ValueChanged != null)
{
ValueChanged (this, new PropertyChangedEventArgs ("Value"));
}
}
}
public static class TreeViewItemExtensions
{
/// <returns>Depth of the given TreeViewItem</returns>
public static int GetDepth (this TreeViewItem item)
{
var tvi = item.GetParent() as TreeViewItem;
if (tvi != null)
return tvi.GetDepth() + 1;
return 0;
}
/// <returns>Control that contains specified TreeViewItem
/// (either TreeView or another TreeViewItem).</returns>
public static ItemsControl GetParent (this TreeViewItem item)
{
return ItemsControl.ItemsControlFromItemContainer (item);
}
}
public class LeftMarginMultiplierConverter : IValueConverter
{
public double Length { get; set; }
public object Convert (object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var item = value as TreeViewItem;
if (item == null)
return new Thickness(0);
double thickness = Length * item.GetDepth();
return new Thickness (thickness, 0, 0, 0);
}
public object ConvertBack (object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new System.NotImplementedException();
}
}
}