work on menu and bindings

This commit is contained in:
stax76
2023-11-04 20:24:10 +01:00
parent b41ca3cd89
commit 1d3fe0a924
11 changed files with 195 additions and 82 deletions

View File

@@ -13,11 +13,7 @@
- C# and PowerShell scripting was removed because of a compatibility problem
with the .NET 6 platform. .NET extensions are supported with a new host
(not backward compatible). A example extension is available under \src\MpvNet.Extension\ExampleExtension
- Redesigned bindings and context menu, the default bindings and context menu
are now defined internally, no longer is a default input.conf file generated.
It means mpv.net no longer loses control over the default bindings and menu.
The new design, is ready for localization. Defining the context menu in
input.conf is still supported, but discouraged and undocumented.
- Redesigned bindings and context menu.
- auto-play option removed, mpv supports it with the option reset-on-next-file.
- Dark mode title bar enabled on Windows 10.0.18985 or higher.
- The navigation bar on the left side of the config editor was changed
@@ -27,6 +23,7 @@
- Improved support for third party osc scripts like uosc.
- Support of the mpv property `focused`.
- Various improvements and fixes in the input bindings editor.
- libmpv zhongfly 2023-11-03
# v6.0.3.2 Beta (2022-10-14)

View File

@@ -141,6 +141,25 @@ https://mpv.io/manual/master/#list-of-input-commands
mpv input options:
https://mpv.io/manual/master/#input
Before version v7 all bindings and the context menu definition
was contained in the input.conf file, which mpv.net created
in case it didn't exist. This had the disadvantage that mpv.net
lost control over all default bindings and the context menu
defaults. This was unfortunate, v7 introduces a new design
fixing it.
In v7 no input.conf file is created, the default bindings and
context menu is defined internally. input.conf only contains
what is different from the internally defined defaults,
so it's the same how mpv is used.
For backward compatibility the old input.conf format with the
menu definition using `#menu: ` is still supported. The new
design also allows for a menu customization, in a sub section
called `Custom`. In input.conf it can be defined like so:
`Ctrl+a show-text Test #custom-menu: Test > Test`
Command Line Interface
----------------------
@@ -550,6 +569,18 @@ features are supported that have an own implementation in mpv.net.
A window free mode is currently not supported, the main window is always
visible, even when mpv.net is started from the terminal and music is played.
For mpv.net it's currently not possible to find out where OSC menus are located,
but there are 3 features that require this information, therefore mpv.net
makes the assumption that near the window borders might be OSC menus. As a result
the following three features, work only when invokes from the center of the window:
1. Window dragging (moving the window with the mouse).
2. Showing the context menu.
3. Auto hiding the mouse cursor.
When the mouse is near a window border, these 3 features are not available.
The dead zone sizes are 10% left, top, right and 22% bottom.
The documentation of mpv's window features can be found here:
https://mpv.io/manual/master/#window

View File

@@ -44,6 +44,7 @@ public class GuiCommand
["move-window"] = args => MoveWindow?.Invoke(args[0]),
["window-scale"] = args => WindowScaleNet?.Invoke(float.Parse(args[0], CultureInfo.InvariantCulture)),
["show-menu"] = args => ShowMenu?.Invoke(),
["show-bindings"] = args => ShowBindings(),
// deprecated
@@ -265,6 +266,14 @@ public class GuiCommand
ProcessHelp.ShellExecute(file);
}
public void ShowBindings()
{
string info = "# mpv.net might modify the input.conf content before it is passed to mpv." + BR +
"# Below are the bindings as they were passed to mpv." + BR2;
ShowTextWithEditor("Bindings", info + Player.UsedInputConfContent);
}
//public void ShowCommandPalette()
//{
// MainForm.Instance?.BeginInvoke(() => {

View File

@@ -13,7 +13,7 @@ public class CommandPalette
.Where(i => i.Command != "")
.Select(i => new CommandPaletteItem()
{
Text = i.Path,
Text = i.Comment,
SecondaryText = i.Input,
Action = () => Core.Command(i.Command),
Binding = i

View File

@@ -92,7 +92,7 @@
<DataGrid.Columns>
<DataGridTextColumn
Header="Name"
Binding="{Binding Path}"
Binding="{Binding Comment}"
MaxWidth="322"
/>

View File

@@ -59,11 +59,11 @@ public partial class InputWindow : Window
return item.Input.ToLower().Contains(searchText);
}
else if (searchText.StartsWith("m ") || searchText.StartsWith("m:"))
return item.Path.ToLower().Contains(searchText.Substring(2).Trim());
return item.Comment.ToLower().Contains(searchText.Substring(2).Trim());
else if (searchText.StartsWith("c ") || searchText.StartsWith("c:"))
return item.Command.ToLower().Contains(searchText.Substring(2).Trim());
else if (item.Command.ToLower().Contains(searchText) ||
item.Path.ToLower().Contains(searchText) ||
item.Comment.ToLower().Contains(searchText) ||
item.Input.ToLower().Contains(searchText))
{
return true;
@@ -120,13 +120,18 @@ public partial class InputWindow : Window
return;
if (App.InputConf.HasMenu)
{
App.InputConf.CreateBackup();
File.WriteAllText(App.InputConf.Path, App.InputConf.Content = newContent);
}
else
{
newContent = InputHelp.ConvertToString(InputHelp.GetReducedBindings(Bindings));
newContent = newContent.Replace("#menu: ", "# ");
App.InputConf.CreateBackup();
File.WriteAllText(App.InputConf.Path, App.InputConf.Content = newContent);
}
Msg.ShowInfo("Changes will be available on next startup.");
}

View File

@@ -30,11 +30,12 @@ public partial class MainForm : Form
public Dictionary<string, WpfControls.MenuItem> MenuItemDuplicate = new Dictionary<string, WpfControls.MenuItem>();
public bool WasShown { get; set; }
public static MainForm? Instance { get; set; }
WpfControls.ContextMenu? ContextMenu { get; set; }
WpfControls.ContextMenu ContextMenu { get; } = new WpfControls.ContextMenu();
AutoResetEvent MenuAutoResetEvent { get; } = new AutoResetEvent(false);
Point _lastCursorPosition;
Taskbar? _taskbar;
Point _mouseDownLocation;
List<Binding>? _confBindings;
int _lastCursorChanged;
int _lastCycleFullscreen;
@@ -253,7 +254,7 @@ public partial class MainForm : Form
ShowCursor();
UpdateMenu();
ContextMenu!.IsOpen = true;
ContextMenu.IsOpen = true;
});
}
@@ -271,7 +272,10 @@ public partial class MainForm : Form
if (!Player.Border)
top = ClientSize.Height * 0.1f;
return pos.Y > ClientSize.Height * 0.78 || pos.X > ClientSize.Width * 0.9 || pos.Y < top;
return pos.X < ClientSize.Width * 0.1 ||
pos.X > ClientSize.Width * 0.9 ||
pos.Y < top ||
pos.Y > ClientSize.Height * 0.78;
}
bool IsCommandPaletteVissible() => CommandPaletteHost != null && CommandPaletteHost.Visible;
@@ -295,10 +299,10 @@ public partial class MainForm : Form
foreach (MediaTrack track in vidTracks)
{
var mi = new WpfControls.MenuItem() { Header = track.Text.Replace("_", "__") };
mi.Click += (sender, args) => Player.CommandV("set", "vid", track.ID.ToString());
mi.IsChecked = Player.VID == track.ID.ToString();
trackMenuItem.Items.Add(mi);
var menuItem = new WpfControls.MenuItem() { Header = track.Text.Replace("_", "__") };
menuItem.Click += (sender, args) => Player.CommandV("set", "vid", track.ID.ToString());
menuItem.IsChecked = Player.VID == track.ID.ToString();
trackMenuItem.Items.Add(menuItem);
}
if (vidTracks.Any())
@@ -306,10 +310,10 @@ public partial class MainForm : Form
foreach (MediaTrack track in audTracks)
{
var mi = new WpfControls.MenuItem() { Header = track.Text.Replace("_", "__") };
mi.Click += (sender, args) => Player.CommandV("set", "aid", track.ID.ToString());
mi.IsChecked = Player.AID == track.ID.ToString();
trackMenuItem.Items.Add(mi);
var menuItem = new WpfControls.MenuItem() { Header = track.Text.Replace("_", "__") };
menuItem.Click += (sender, args) => Player.CommandV("set", "aid", track.ID.ToString());
menuItem.IsChecked = Player.AID == track.ID.ToString();
trackMenuItem.Items.Add(menuItem);
}
if (subTracks.Any())
@@ -317,18 +321,18 @@ public partial class MainForm : Form
foreach (MediaTrack track in subTracks)
{
var mi = new WpfControls.MenuItem() { Header = track.Text.Replace("_", "__") };
mi.Click += (sender, args) => Player.CommandV("set", "sid", track.ID.ToString());
mi.IsChecked = Player.SID == track.ID.ToString();
trackMenuItem.Items.Add(mi);
var menuItem = new WpfControls.MenuItem() { Header = track.Text.Replace("_", "__") };
menuItem.Click += (sender, args) => Player.CommandV("set", "sid", track.ID.ToString());
menuItem.IsChecked = Player.SID == track.ID.ToString();
trackMenuItem.Items.Add(menuItem);
}
if (subTracks.Any())
{
var mi = new WpfControls.MenuItem() { Header = "S: No subtitles" };
mi.Click += (sender, args) => Player.CommandV("set", "sid", "no");
mi.IsChecked = Player.SID == "no";
trackMenuItem.Items.Add(mi);
var menuItem = new WpfControls.MenuItem() { Header = "S: No subtitles" };
menuItem.Click += (sender, args) => Player.CommandV("set", "sid", "no");
menuItem.IsChecked = Player.SID == "no";
trackMenuItem.Items.Add(menuItem);
}
if (ediTracks.Any())
@@ -336,10 +340,10 @@ public partial class MainForm : Form
foreach (MediaTrack track in ediTracks)
{
var mi = new WpfControls.MenuItem() { Header = track.Text.Replace("_", "__") };
mi.Click += (sender, args) => Player.CommandV("set", "edition", track.ID.ToString());
mi.IsChecked = Player.Edition == track.ID;
trackMenuItem.Items.Add(mi);
var menuItem = new WpfControls.MenuItem() { Header = track.Text.Replace("_", "__") };
menuItem.Click += (sender, args) => Player.CommandV("set", "edition", track.ID.ToString());
menuItem.IsChecked = Player.Edition == track.ID;
trackMenuItem.Items.Add(menuItem);
}
}
}
@@ -352,14 +356,16 @@ public partial class MainForm : Form
foreach (Chapter chapter in Player.GetChapters())
{
var mi = new WpfControls.MenuItem
var menuItem = new WpfControls.MenuItem
{
Header = chapter.Title,
InputGestureText = chapter.TimeDisplay
};
mi.Click += (sender, args) => Player.CommandV("seek", chapter.Time.ToString(CultureInfo.InvariantCulture), "absolute");
chaptersMenuItem.Items.Add(mi);
menuItem.Click += (sender, args) =>
Player.CommandV("seek", chapter.Time.ToString(CultureInfo.InvariantCulture), "absolute");
chaptersMenuItem.Items.Add(menuItem);
}
}
@@ -372,10 +378,10 @@ public partial class MainForm : Form
foreach (string path in App.Settings.RecentFiles)
{
var file = AppClass.GetTitleAndPath(path);
var mi = MenuHelp.Add(recentMenuItem.Items, file.Title.ShortPath(100));
var menuItem = MenuHelp.Add(recentMenuItem.Items, file.Title.ShortPath(100));
if (mi != null)
mi.Click += (sender, args) =>
if (menuItem != null)
menuItem.Click += (sender, args) =>
Player.LoadFiles(new[] { file.Path }, true, false);
}
@@ -406,12 +412,12 @@ public partial class MainForm : Form
{
if (item.Length != TimeSpan.Zero)
{
var mi = MenuHelp.Add(titlesMenuItem.Items, $"Title {item.Index + 1}");
var menuItem = MenuHelp.Add(titlesMenuItem.Items, $"Title {item.Index + 1}");
if (mi != null)
if (menuItem != null)
{
mi.InputGestureText = item.Length.ToString();
mi.Click += (sender, args) => Player.SetBluRayTitle(item.Index);
menuItem.InputGestureText = item.Length.ToString();
menuItem.Click += (sender, args) => Player.SetBluRayTitle(item.Index);
}
}
}
@@ -420,19 +426,17 @@ public partial class MainForm : Form
var profilesMenuItem = FindMenuItem("Profile");
if (profilesMenuItem != null)
if (profilesMenuItem != null && !profilesMenuItem.HasItems)
{
profilesMenuItem.Items.Clear();
foreach (string profile in Player.ProfileNames)
{
if (!profile.StartsWith("extension."))
{
var mi = MenuHelp.Add(profilesMenuItem.Items, profile);
var menuItem = MenuHelp.Add(profilesMenuItem.Items, profile);
if (mi != null)
if (menuItem != null)
{
mi.Click += (sender, args) =>
menuItem.Click += (sender, args) =>
{
Player.CommandV("show-text", profile);
Player.CommandV("apply-profile", profile);
@@ -441,9 +445,38 @@ public partial class MainForm : Form
}
}
}
var customMenuItem = FindMenuItem("Custom");
if (customMenuItem != null)
{
if (!customMenuItem.HasItems)
{
var customBindings = _confBindings!.Where(it => it.IsCustomMenu);
if (customBindings.Any())
{
foreach (Binding binding in customBindings)
{
var menuItem = MenuHelp.Add(customMenuItem.Items, binding.Comment);
if (menuItem != null)
{
menuItem.Click += (sender, args) => Player.Command(binding.Command);
menuItem.InputGestureText = binding.Input;
}
}
}
else
{
if (ContextMenu.Items.Contains(customMenuItem))
ContextMenu.Items.Remove(customMenuItem);
}
}
}
}
public WpfControls.MenuItem? FindMenuItem(string text) => FindMenuItem(text, ContextMenu?.Items);
public WpfControls.MenuItem? FindMenuItem(string text) => FindMenuItem(text, ContextMenu.Items);
WpfControls.MenuItem? FindMenuItem(string text, WpfControls.ItemCollection? items)
{
@@ -729,22 +762,24 @@ public partial class MainForm : Form
public void InitAndBuildContextMenu()
{
ContextMenu = new WpfControls.ContextMenu();
ContextMenu.Closed += ContextMenu_Closed;
ContextMenu.UseLayoutRounding = true;
foreach (Binding binding in App.InputConf.GetMenuBindings())
var (menuBindings, confBindings) = App.InputConf.GetBindings();
_confBindings = confBindings;
foreach (Binding binding in menuBindings)
{
if (!binding.IsMenu)
continue;
Binding tempBinding = binding;
var menuItem = MenuHelp.Add(ContextMenu?.Items, tempBinding.Path);
var menuItem = MenuHelp.Add(ContextMenu.Items, tempBinding.Comment);
if (menuItem != null)
{
MenuItemDuplicate[tempBinding.Path] = menuItem;
MenuItemDuplicate[tempBinding.Comment] = menuItem;
menuItem.Click += (sender, args) => {
try {
TaskHelp.Run(() => {
@@ -915,10 +950,10 @@ public partial class MainForm : Form
{
Keys keyCode = (Keys)(int)m.WParam & Keys.KeyCode;
if (keyCode == Keys.Escape && _contextMenuIsReady && ContextMenu!.IsOpen)
if (keyCode == Keys.Escape && _contextMenuIsReady && ContextMenu.IsOpen)
{
ignore = true;
ContextMenu!.IsOpen = false;
ContextMenu.IsOpen = false;
}
}
@@ -1097,7 +1132,7 @@ public partial class MainForm : Form
else if ((Environment.TickCount - _lastCursorChanged > 1500 ||
Environment.TickCount - _lastCursorChanged > 5000) &&
ClientRectangle.Contains(PointToClient(MousePosition)) &&
ActiveForm == this && !ContextMenu!.IsVisible && !IsMouseInOsc() &&
ActiveForm == this && !ContextMenu.IsVisible && !IsMouseInOsc() &&
!IsCommandPaletteVissible())
HideCursor();

View File

@@ -5,17 +5,17 @@ namespace MpvNet;
public class Binding : ObservableObject
{
public string Path { get; set; }
public string Command { get; set; }
public string Comment { get; set; }
public bool IsCustomMenu { get; set; }
public bool IsMenu { get; set; }
string _input = "";
public Binding()
{
Path = Command = Comment = Input = "";
Command = Comment = Input = "";
}
public Binding(string folder = "",
@@ -26,20 +26,19 @@ public class Binding : ObservableObject
{
if (folder != "" && name != "")
{
Path = folder + " > " + name;
Comment = folder + " > " + name;
IsMenu = true;
}
else if (name != "")
{
Path = name;
Comment = name;
IsMenu = true;
}
else
Path = "";
Comment = comment;
Command = command;
Input = input;
Comment = comment == "" ? Path : comment;
}
public string Input
@@ -48,5 +47,5 @@ public class Binding : ObservableObject
set => SetProperty(ref _input, value);
}
public bool IsEmpty() => Path == "" && Command == "" && Comment == "" && Input == "";
public bool IsEmpty() => Command == "" && Comment == "" && Input == "";
}

View File

@@ -1,4 +1,5 @@

using MpvNet.ExtensionMethod;
using MpvNet.Help;
namespace MpvNet;
@@ -20,12 +21,12 @@ public class InputConf
public bool HasMenu => _hasMenu ??= Content.Contains("#menu:");
public List<Binding> GetMenuBindings()
public (List<Binding> menuBindings, List<Binding>? confBindings) GetBindings()
{
var confbindings = InputHelp.Parse(Content);
if (HasMenu)
return confbindings;
return (confbindings, confbindings);
var defaultBindings = InputHelp.GetDefaults();
@@ -42,7 +43,7 @@ public class InputConf
if (defaultBinding.Command == confBinding.Command)
defaultBinding.Input = confBinding.Input;
return defaultBindings;
return (defaultBindings, confbindings);
}
public string GetContent()
@@ -71,4 +72,15 @@ public class InputConf
return InputHelp.ConvertToString(defaults);
}
}
public void CreateBackup()
{
if (!File.Exists(Path))
return;
string targetPath = System.IO.Path.GetTempPath().AddSep() +
"mpv.net input.conf backup " + Guid.NewGuid() + ".conf";
File.Copy(Path, targetPath);
}
}

View File

@@ -126,6 +126,7 @@ public static class InputHelp
new (_("View > Advanced"), _("Show Commands"), "script-message-to mpvnet show-commands", "C"),
new (_("View > Advanced"), _("Show Demuxers"), "script-message-to mpvnet show-demuxers"),
new (_("View > Advanced"), _("Show Decoders"), "script-message-to mpvnet show-decoders"),
new (_("View > Advanced"), _("Show Bindings"), "script-message-to mpvnet show-bindings"),
new ("", _("Profile")),
new (_("Settings"), _("Show Config Editor"), "script-message-to mpvnet show-conf-editor", "Ctrl+,"),
new (_("Settings"), _("Show Input Editor"), "script-message-to mpvnet show-input-editor", "Ctrl+i"),
@@ -141,6 +142,7 @@ public static class InputHelp
new (_("Tools"), _("Exit"), "quit", "Esc"),
new (_("Tools"), _("Exit Watch Later"), "quit-watch-later", "Q"),
new (_("Tools"), _("Show current file in File Explorer"), @"run powershell -command ""explorer.exe '/select,' ( \""${path}\"" -replace '/', '\\' )""", "e"),
new ("", _("Custom")),
new (_("Help"), _("Website mpv"), "script-message-to mpvnet shell-execute https://mpv.io"),
new (_("Help"), _("Website mpv.net"), "script-message-to mpvnet shell-execute https://github.com/mpvnet-player/mpv.net"),
new (_("Help"), "-"),
@@ -204,7 +206,7 @@ public static class InputHelp
if (binding.Comment != "" &&
binding.Command == "" &&
binding.Input == "" &&
binding.Path == "")
!binding.IsMenu)
{
sb.AppendLine("#" + binding.Comment.Trim());
continue;
@@ -212,13 +214,28 @@ public static class InputHelp
string command = binding.Command.Trim();
string input = binding.Input.Trim();
string comment = binding.IsMenu ? "menu: " + binding.Path : binding.Path.Trim();
input = input == "" ? "_" : input;
string line = input.PadRight(10) + " ";
line += command == "" ? "ignore" : command;
string comment;
if (binding.IsMenu)
comment = "menu: " + binding.Comment.Trim();
else if (binding.IsCustomMenu)
comment = "custom-menu: " + binding.Comment.Trim();
else
comment = binding.Comment.Trim();
if (comment != "")
line = line.PadRight(40) + " #" + comment;
{
if (comment.StartsWith("menu: ") || comment.StartsWith("custom-menu: "))
comment = " #" + comment;
else
comment = " # " + comment;
line = line.PadRight(40) + comment;
}
sb.AppendLine(line);
}
@@ -276,17 +293,25 @@ public static class InputHelp
if (line.Contains("#menu:"))
{
binding.Path = line[(line.IndexOf("#menu:") + 6)..].Trim();
binding.Comment = binding.Path;
binding.Comment = line[(line.IndexOf("#menu:") + 6)..].Trim();
binding.IsMenu = true;
line = line[..line.IndexOf("#menu:")];
if (binding.Path.Contains(';'))
binding.Path = binding.Path[(binding.Path.IndexOf(";") + 1)..].Trim();
}
//else if (line.Contains("#!"))
//{
// binding.Comment = line[(line.IndexOf("#!") + 2)..].Trim();
// binding.IsMenu = true;
// line = line[..line.IndexOf("#!")];
//}
else if (line.Contains("#custom-menu:"))
{
binding.Comment = line[(line.IndexOf("#custom-menu:") + 13)..].Trim();
binding.IsCustomMenu = true;
line = line[..line.IndexOf("#custom-menu:")];
}
else if (line.Contains('#'))
{
binding.Path = line[(line.IndexOf("#") + 1)..].Trim();
binding.Comment = line[(line.IndexOf("#") + 1)..].Trim();
line = line[..line.IndexOf("#")];
}
@@ -378,11 +403,11 @@ public static class InputHelp
if (value.Contains("#menu:"))
{
binding.Path = value[(value.IndexOf("#menu:") + 6)..].Trim();
binding.Comment = value[(value.IndexOf("#menu:") + 6)..].Trim();
value = value[..value.IndexOf("#menu:")];
if (binding.Path.Contains(';'))
binding.Path = binding.Path[(binding.Path.IndexOf(";") + 1)..].Trim();
if (binding.Comment.Contains(';'))
binding.Comment = binding.Comment[(binding.Comment.IndexOf(";") + 1)..].Trim();
}
binding.Command = value.Trim();

View File

@@ -1,7 +1,6 @@

using System.Drawing;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
@@ -23,6 +22,7 @@ public class MainPlayer : MpvClient
public string GPUAPI { get; set; } = "auto";
public string Path { get; set; } = "";
public string VO { get; set; } = "gpu";
public string UsedInputConfContent { get; set; } = "";
public string VID { get; set; } = "";
public string AID { get; set; } = "";
@@ -101,8 +101,8 @@ public class MainPlayer : MpvClient
SetPropertyString("osc", "yes");
SetPropertyString("force-window", "yes");
SetPropertyString("config-dir", ConfigFolder);
SetPropertyString("config", "yes");
SetPropertyString("input-conf", @"memory://" + App.InputConf.GetContent());
SetPropertyString("config", "yes");
SetPropertyString("input-conf", @"memory://" + (UsedInputConfContent = App.InputConf.GetContent()));
ProcessCommandLine(true);