Skip to content

Commit 426d3e6

Browse files
author
Carl Chang
committed
move immersion mode to application wide settings;
bug fixes; adding custom context menu;
1 parent 200b9fb commit 426d3e6

12 files changed

+282
-164
lines changed

App.xaml.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public partial class App : Application
4646
public static readonly int MaxLoadThreads = Environment.ProcessorCount;
4747
public static System.Threading.SemaphoreSlim LoadThrottle = new System.Threading.SemaphoreSlim(MaxLoadThreads);
4848

49+
public static ContextMenuWindow ContextMenuWin;
50+
4951
private void App_Startup(object sender, StartupEventArgs e) {
5052
try {
5153
Setting.LoadConfigFromFile();
@@ -110,6 +112,9 @@ private void App_Startup(object sender, StartupEventArgs e) {
110112
});
111113
#endif
112114

115+
//handle immersion mode change
116+
Setting.StaticPropertyChanged += Setting_StaticPropertyChanged;
117+
113118
//show mainwindow
114119
new MainWindow() {
115120
Width = Setting.LastWindowSize.Width,
@@ -122,6 +127,14 @@ private void App_Startup(object sender, StartupEventArgs e) {
122127
}
123128
}
124129

130+
private void Setting_StaticPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) {
131+
if (e.PropertyName == nameof(Setting.ImmersionMode)) {
132+
foreach (var win in Windows) {
133+
if (!(win is MainWindow mainWin)) continue;
134+
Task.Run(() => mainWin.LoadPath(mainWin.CurrentPath));
135+
}
136+
}
137+
}
125138

126139
private void App_Exit(object sender, ExitEventArgs e) {
127140
while (LoadThrottle.CurrentCount < MaxLoadThreads) {
@@ -131,6 +144,8 @@ private void App_Exit(object sender, ExitEventArgs e) {
131144

132145
Setting.SaveConfigToFile();
133146

147+
Setting.StaticPropertyChanged -= Setting_StaticPropertyChanged;
148+
134149
//db maintenance
135150
Execute(con => {
136151
using (var cmd = new SQLiteCommand(con)) {

ContextMenuWindow.xaml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<local:RoundedWindow x:Class="ZipImageViewer.ContextMenuWindow"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
5+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6+
xmlns:fa="http://schemas.fontawesome.com/icons/"
7+
xmlns:local="clr-namespace:ZipImageViewer" CloseBehavior="FadeOutAndHide" ButtonCloseVisible="False" ShowInTaskbar="False"
8+
mc:Ignorable="d" ResizeMode="NoResize" SizeToContent="WidthAndHeight"
9+
Background="#FF444444" Foreground="LightGray">
10+
<Window.Resources>
11+
<Style TargetType="fa:ImageAwesome">
12+
<Setter Property="Width" Value="24"/>
13+
<Setter Property="Height" Value="24"/>
14+
<Setter Property="Foreground" Value="LightGray"/>
15+
</Style>
16+
</Window.Resources>
17+
<local:PaddedGrid Padding="2">
18+
<Grid.RowDefinitions>
19+
<RowDefinition/>
20+
<RowDefinition/>
21+
</Grid.RowDefinitions>
22+
<Grid.Resources>
23+
<Style TargetType="Border" BasedOn="{StaticResource S_RiseOnHover}">
24+
<Setter Property="Padding" Value="5"/>
25+
<Setter Property="CornerRadius" Value="4"/>
26+
<EventSetter Event="PreviewMouseDown" Handler="Menu_PreviewMouseUp"/>
27+
<Style.Triggers>
28+
<Trigger Property="IsMouseOver" Value="True">
29+
<Setter Property="Background" Value="#22FFFFFF" />
30+
</Trigger>
31+
</Style.Triggers>
32+
</Style>
33+
</Grid.Resources>
34+
35+
<Border Grid.Row="0" x:Name="B_OpenInExplorer">
36+
<StackPanel Orientation="Horizontal">
37+
<fa:ImageAwesome Icon="Regular_WindowMaximize" ToolTip="Open in Explorer"/>
38+
<TextBlock Text="Open in Explorer" FontWeight="Bold" VerticalAlignment="Center" Margin="10 0 0 0"/>
39+
</StackPanel>
40+
</Border>
41+
<Border Grid.Row="1" x:Name="B_OpenInNewWindow">
42+
<StackPanel Orientation="Horizontal">
43+
<fa:ImageAwesome Icon="Regular_WindowRestore" ToolTip="Open in Explorer"/>
44+
<TextBlock Text="Open in New Window" FontWeight="Bold" VerticalAlignment="Center" Margin="10 0 0 0"/>
45+
</StackPanel>
46+
</Border>
47+
</local:PaddedGrid>
48+
</local:RoundedWindow>

ContextMenuWindow.xaml.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using System.Windows;
7+
using System.Windows.Controls;
8+
using System.Windows.Data;
9+
using System.Windows.Documents;
10+
using System.Windows.Input;
11+
using System.Windows.Media;
12+
using System.Windows.Media.Imaging;
13+
using System.Windows.Shapes;
14+
15+
namespace ZipImageViewer
16+
{
17+
public partial class ContextMenuWindow : RoundedWindow
18+
{
19+
public ContextMenuWindow() {
20+
InitializeComponent();
21+
}
22+
23+
public ObjectInfo ObjectInfo;
24+
25+
private void Menu_PreviewMouseUp(object sender, MouseButtonEventArgs e) {
26+
if (ObjectInfo == null) return;
27+
if (!(Owner is MainWindow mainWin)) return;
28+
29+
var border = (Border)sender;
30+
switch (border.Name) {
31+
case nameof(B_OpenInExplorer):
32+
Helpers.Run("explorer", $"/select, \"{ObjectInfo.FileSystemPath}\"");
33+
Close();
34+
break;
35+
case nameof(B_OpenInNewWindow):
36+
if (ObjectInfo.Flags.HasFlag(FileFlags.Image)) {
37+
mainWin.LoadPath(ObjectInfo);
38+
}
39+
else if (ObjectInfo.Flags.HasFlag(FileFlags.Directory) ||
40+
ObjectInfo.Flags.HasFlag(FileFlags.Archive)) {
41+
var win = new MainWindow {
42+
InitialPath = ObjectInfo.FileSystemPath
43+
};
44+
win.Show();
45+
}
46+
Close();
47+
break;
48+
}
49+
50+
ObjectInfo = null;
51+
}
52+
53+
54+
}
55+
}

Helpers/ObservableClasses.cs

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
namespace ZipImageViewer {
1717
public class ObservableKeyedCollection<TKey, TItem> : KeyedCollection<TKey, TItem>, INotifyCollectionChanged, INotifyPropertyChanged {
18-
private readonly Func<TItem, TKey> _getKeyForItemDelegate;
18+
public readonly Func<TItem, TKey> GetKeyForItemDelegate;
1919
private readonly PropertyChangingEventHandler ChildPropertyChanging;
2020
private readonly PropertyChangedEventHandler ChildPropertyChanged;
2121
private readonly string keyPropertyName;
@@ -25,11 +25,11 @@ public class ObservableKeyedCollection<TKey, TItem> : KeyedCollection<TKey, TIte
2525
/// Using a delegate should be faster than the property name which will use reflection.
2626
/// If TItem implements both INotifyCollectionChanged and INotifyCollectionChanging, the name of the key property is also needed for key updating to work.
2727
/// </summary>
28-
public ObservableKeyedCollection(Func<TItem, TKey> getKeyFunc = null, string keyPropName = null) {
28+
public ObservableKeyedCollection(Func<TItem, TKey> getKeyFunc = null, string keyPropName = null, IEnumerable<TItem> collection = null) {
2929
if (getKeyFunc == null && keyPropName == null)
30-
throw new ArgumentException(@"getKeyForItemDelegate and KeyPropertyName cannot both be null.");
30+
throw new ArgumentException(@"GetKeyForItemDelegate and KeyPropertyName cannot both be null.");
3131
keyPropertyName = keyPropName;
32-
_getKeyForItemDelegate = getKeyFunc;
32+
GetKeyForItemDelegate = getKeyFunc;
3333

3434
if (keyPropertyName != null &&
3535
typeof(TItem).GetInterface(nameof(INotifyPropertyChanged)) != null &&
@@ -45,22 +45,22 @@ public ObservableKeyedCollection(Func<TItem, TKey> getKeyFunc = null, string key
4545
Dictionary?.Add(GetKeyForItem(item), item);
4646
};
4747
}
48-
}
4948

50-
public ObservableKeyedCollection(Func<TItem, TKey> getKeyFunc, IEnumerable < TItem> collection) : this(getKeyFunc) {
51-
IList<TItem> items = Items;
52-
if (collection != null && items != null) {
53-
using (IEnumerator<TItem> enumerator = collection.GetEnumerator()) {
54-
while (enumerator.MoveNext()) {
55-
items.Add(enumerator.Current);
49+
if (collection != null) {
50+
IList<TItem> items = Items;
51+
if (collection != null && items != null) {
52+
using (IEnumerator<TItem> enumerator = collection.GetEnumerator()) {
53+
while (enumerator.MoveNext()) {
54+
items.Add(enumerator.Current);
55+
}
5656
}
5757
}
5858
}
59-
}
59+
}
6060

6161
protected override TKey GetKeyForItem(TItem item) {
62-
if (_getKeyForItemDelegate != null)//delegate is faster than reflection.
63-
return _getKeyForItemDelegate(item);
62+
if (GetKeyForItemDelegate != null)//delegate is faster than reflection.
63+
return GetKeyForItemDelegate(item);
6464
if (keyPropertyName != null)
6565
return (TKey)item.GetType().GetProperty(keyPropertyName).GetValue(item);
6666
throw new ArgumentException(@"getKeyForItemDelegate and KeyPropertyName cannot both be null.");
@@ -231,7 +231,7 @@ public T Item {
231231
}
232232

233233
public override string ToString() {
234-
return item.ToString();
234+
return item?.ToString();
235235
}
236236

237237
public override bool Equals(object obj) {
@@ -259,16 +259,18 @@ public Observable(T i) {
259259
}
260260
}
261261

262-
public class ObservablePair<T1, T2> : INotifyPropertyChanged, IEquatable<ObservablePair<T1, T2>>
262+
public class ObservablePair<T1, T2> : INotifyPropertyChanged, INotifyPropertyChanging, IEquatable<ObservablePair<T1, T2>>
263263
{
264264
public event PropertyChangedEventHandler PropertyChanged;
265+
public event PropertyChangingEventHandler PropertyChanging;
265266

266-
private T1 item1;
267+
private T1 item1;
267268
public T1 Item1 {
268269
get => item1;
269270
set {
270271
if (value == null && item1 == null) return;
271272
if (item1 != null && item1.Equals(value)) return;
273+
PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(Item1)));
272274
item1 = value;
273275
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Item1)));
274276
}
@@ -280,13 +282,14 @@ public T2 Item2 {
280282
set {
281283
if (value == null && item2 == null) return;
282284
if (item2 != null && item2.Equals(value)) return;
285+
PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(Item2)));
283286
item2 = value;
284287
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Item2)));
285288
}
286289
}
287290

288291
public override string ToString() {
289-
return $@"({item1.ToString()}, {item2.ToString()})";
292+
return $@"({item1?.ToString()}, {item2?.ToString()})";
290293
}
291294

292295
public override bool Equals(object obj) {

Helpers/Setting.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,16 @@ public static ObservableKeyedCollection<string, ObservablePair<string, string>>
155155
}
156156
}
157157

158-
//public static Dictionary<string, string> MappedPasswords;
158+
private static bool immersionMode;
159+
//this one is not saved
160+
public static bool ImmersionMode {
161+
get => immersionMode;
162+
set {
163+
if (immersionMode == value) return;
164+
immersionMode = value;
165+
OnStaticPropertyChanged(nameof(ImmersionMode));
166+
}
167+
}
159168

160169

161170
public static void LoadConfigFromFile(string path = "config.ini") {
@@ -191,9 +200,9 @@ public static void LoadConfigFromFile(string path = "config.ini") {
191200
}
192201

193202
//parse saved passwords at last
194-
FallbackPasswords = new ObservableKeyedCollection<string, Observable<string>>(o => o.Item,
203+
FallbackPasswords = new ObservableKeyedCollection<string, Observable<string>>(o => o.Item, null,
195204
iniData["Saved Passwords"].Where(d => d.Value.Length == 0).Select(d => new Observable<string>(d.KeyName)));
196-
MappedPasswords = new ObservableKeyedCollection<string, ObservablePair<string, string>>(p => p.Item1,
205+
MappedPasswords = new ObservableKeyedCollection<string, ObservablePair<string, string>>(p => p.Item1, "Item1",
197206
iniData["Saved Passwords"].Where(d => d.Value.Length > 0).Select(d => new ObservablePair<string, string>(d.KeyName, d.Value)));
198207

199208
//apply dll path

MainWindow.xaml

Lines changed: 12 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -10,73 +10,36 @@
1010
Icon="Resources/ZipImageViewer.ico"
1111
mc:Ignorable="d" UseLayoutRounding="True" Background="#FF1F1F1F" Foreground="LightGray"
1212
Title="{Binding ElementName=MainWin, Path=CurrentPath, Mode=OneWay}" Height="640" Width="1200" Drop="MainWin_Drop" AllowDrop="True"
13-
Loaded="MainWin_Loaded" Unloaded="MainWin_Unloaded" DpiChanged="MainWin_DpiChanged">
13+
Loaded="MainWin_Loaded" Closing="MainWin_Closing" DpiChanged="MainWin_DpiChanged">
1414
<Window.Resources>
1515
<CollectionViewSource x:Key="ObjectListViewSource"
1616
Source="{Binding ElementName=MainWin, Path=ObjectList, Mode=OneTime}">
1717
</CollectionViewSource>
1818
<local:CustomCmdArgsConverter x:Key="CustomCmdArgsConverter"/>
19-
<ContextMenu x:Key="ThumbContextMenu" Opened="CTM_Opened">
20-
<ContextMenu.Resources>
21-
<!--<CollectionViewSource x:Key="MenuItemViewSource" Source="{Binding Source={x:Static local:App.Setting}, Path=CustomCommands, Mode=OneWay}"/>-->
22-
<Style TargetType="MenuItem">
23-
<EventSetter Event="Click" Handler="CTM_Click"/>
24-
<Setter Property="CommandParameter" Value="{Binding Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"/>
25-
</Style>
26-
</ContextMenu.Resources>
27-
<MenuItem Header="View in Explorer">
28-
<MenuItem.Icon>
29-
<fa:ImageAwesome Icon="Solid_FolderOpen" HorizontalAlignment="Center" VerticalAlignment="Center"/>
30-
</MenuItem.Icon>
31-
</MenuItem>
32-
<MenuItem Header="Open in New Window">
33-
<MenuItem.Icon>
34-
<fa:ImageAwesome Icon="Solid_WindowRestore" HorizontalAlignment="Center" VerticalAlignment="Center"/>
35-
</MenuItem.Icon>
36-
</MenuItem>
37-
<!--<MenuItem Header="Custom Commands" ItemsSource="{Binding Source={StaticResource MenuItemViewSource}, Mode=OneTime}">
38-
<MenuItem.ItemTemplate>
39-
<DataTemplate>
40-
<StackPanel>
41-
<StackPanel Orientation="Horizontal">
42-
<TextBlock Text="Program: " FontWeight="Bold"/>
43-
<TextBlock Text="{Binding Item1, Mode=OneWay}"/>
44-
</StackPanel>
45-
<StackPanel Orientation="Horizontal">
46-
<TextBlock Text="Arguments: " FontWeight="Bold"/>
47-
<TextBlock>
48-
<TextBlock.Text>
49-
<MultiBinding Converter="{StaticResource CustomCmdArgsConverter}">
50-
<Binding Path="Item2" Mode="OneWay"/>
51-
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType=ContextMenu}"
52-
Path="Tag" Mode="OneWay"/>
53-
</MultiBinding>
54-
</TextBlock.Text>
55-
</TextBlock>
56-
</StackPanel>
57-
</StackPanel>
58-
</DataTemplate>
59-
</MenuItem.ItemTemplate>
60-
</MenuItem>-->
61-
</ContextMenu>
6219

6320
<DataTemplate x:Key="ThumbDataTemplate" DataType="{x:Type local:ObjectInfo}">
6421
<StackPanel x:Name="SP1" Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center" UseLayoutRounding="True">
6522
<local:Thumbnail x:Name="TN1" RenderTransformOrigin="0.5 0.5" Panel.ZIndex="1" PreviewMouseDown="TN1_Click"
6623
ObjectInfo="{Binding Mode=OneTime}" Margin="10"
6724
Width="{Binding ElementName=MainWin, Path=ThumbRealWidth, Mode=OneWay}"
68-
Height="{Binding ElementName=MainWin, Path=ThumbRealHeight, Mode=OneWay}"
69-
ContextMenu="{StaticResource ThumbContextMenu}">
70-
<!--FlagIconVisibility="{Binding ElementName=MainWin, Path=AuxVisibility, Mode=OneWay}">-->
25+
Height="{Binding ElementName=MainWin, Path=ThumbRealHeight, Mode=OneWay}">
7126
<local:Thumbnail.Style>
7227
<Style TargetType="local:Thumbnail" BasedOn="{StaticResource S_RiseOnHover}"/>
7328
</local:Thumbnail.Style>
7429
</local:Thumbnail>
7530

7631
<TextBlock Text="{Binding DisplayName, Mode=OneTime}" FontSize="10" Foreground="Gray"
7732
MaxWidth="{Binding ElementName=MainWin, Path=ThumbRealWidth, Mode=OneWay}"
78-
HorizontalAlignment="Center" Panel.ZIndex="0" TextWrapping="Wrap"
79-
Visibility="{Binding ElementName=MainWin, Path=AuxVisibility, Mode=OneWay}">
33+
HorizontalAlignment="Center" Panel.ZIndex="0" TextWrapping="Wrap">
34+
<TextBlock.Style>
35+
<Style TargetType="TextBlock">
36+
<Style.Triggers>
37+
<DataTrigger Binding="{Binding ImmersionMode, Source={x:Static local:App.Setting}, Mode=OneWay}" Value="True">
38+
<Setter Property="Visibility" Value="Collapsed"/>
39+
</DataTrigger>
40+
</Style.Triggers>
41+
</Style>
42+
</TextBlock.Style>
8043
</TextBlock>
8144
</StackPanel>
8245
</DataTemplate>

0 commit comments

Comments
 (0)