Hi,
I guess with an english keyboard this problem is rarely to come up.
But with my french layout I have to use right alt key as a modifier for characters like “ { } @ …”
When I want to write “{0;1}” inn input “P” of node Tree Branch by example :
I open context menu,
click Set Path,
click in text box,
hit right alt … It kicks me out.
It happens as soon as I need a special character (node name, group name, set data items, …)
My workaround until now is two copy the special character in a text editor and paste it.
I’m sure you understand my pain…
Is there something I can do to disable the alt “hint” option ?
I’m using a Swiss/French keyboard layout, which means that I also use the ALT + … to write curly braces, square brackets, etc. The latest Rhino for Mac doesn’t seem to have that problem though. The “hint feature” must not be implemented for macOS.
Hi,
Someone can tell me if this can be resolved ?
Is there any hope to have an option to enable/disable alt-key hint behavior to allow
special characters to be used normally ?
Should work as a workaround. But I don’t have a keyboard with AltGr so I cannot test it. On my PC it will filter all Alt keys. It is still kind of buggy sometimes. Here’s the code. It uses tons of Reflection and Harmony hook to filter events .
The code below is licensed under Apache Licene 2.0
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using Grasshopper;
using Grasshopper.GUI;
using Harmony;
namespace PancakeDev.Replacement
{
public static class CtrlAltOverride
{
private const Keys ModifierKey = Keys.Alt;
private static readonly HashSet<ToolStripDropDown> MenuStrips = new HashSet<ToolStripDropDown>();
private static bool InCleanup = false;
private static void AddStripMenu(ToolStripDropDown dropdown)
{
if (InCleanup)
return;
MenuStrips.Add(dropdown);
}
private static void RemoveStripMenu(ToolStripDropDown strip, bool removeEventOnly = false)
{
strip.Closing -= EvtMenuClosing;
strip.Closed -= EvtMenuClosed;
if (strip is ContextMenuStrip context)
context.Opened -= EvtMenuOpened;
if (!removeEventOnly && !InCleanup)
MenuStrips.Remove(strip);
}
private static bool CheckKeys(Keys key)
{
return (key & ModifierKey) == ModifierKey;
}
private static bool CheckContextMenuVisible()
{
return MenuStrips.Any(c => c.Visible);
}
private static void EvtMenuOpened(object sender, EventArgs e)
{
var strip = (ContextMenuStrip)sender;
foreach (var it in strip.Items)
{
if (it is ToolStripDropDownItem dropItem && dropItem.HasDropDownItems)
{
dropItem.DropDown.Closing += EvtMenuClosing;
dropItem.DropDown.Closed += EvtMenuClosed;
AddStripMenu(dropItem.DropDown);
}
}
}
private static void EvtMenuClosing(object sender, ToolStripDropDownClosingEventArgs e)
{
if (e.CloseReason == ToolStripDropDownCloseReason.Keyboard && CheckKeys(Control.ModifierKeys))
{
e.Cancel = true;
}
}
private static void EvtMenuClosed(object sender, ToolStripDropDownClosedEventArgs e)
{
if (sender is ToolStripDropDown strip)
RemoveStripMenu(strip);
}
private static void PostfixMenuCtor(object __instance)
{
var strip = (ContextMenuStrip)__instance;
strip.Opened += EvtMenuOpened;
strip.Closing += EvtMenuClosing;
strip.Closed += EvtMenuClosed;
AddStripMenu(strip);
}
private static bool PrefixKeyDownEvent(object sender, KeyEventArgs e)
{
if (CheckKeys(e.Modifiers) && CheckContextMenuVisible())
{
e.Handled = true;
return false;
}
return true;
}
private static void FallbackCleanup(object sender, MouseEventArgs e)
{
InCleanup = true;
foreach(var it in MenuStrips)
{
RemoveStripMenu(it, true);
if (!(it is ContextMenuStrip))
continue;
it.Close();
}
MenuStrips.Clear();
InCleanup = false;
}
public static bool ProcessHook(HarmonyInstance harmony)
{
var ghTypeMenu = typeof(Grasshopper.GUI.Canvas.GH_Capsule).Assembly.GetTypes().FirstOrDefault(
t => t.FullName == "Grasshopper.GUI.Canvas.GH_CanvasMenuStrip");
var ghMethodMenuCtor = ghTypeMenu?.GetConstructor(new Type[] { });
var ghMethodKeyDownEvent = typeof(GH_DocumentEditor).GetMethod("EditorKeyDown",
BindingFlags.Instance | BindingFlags.NonPublic);
var methodRegisterContextMenu = typeof(CtrlAltOverride).GetMethod(nameof(PostfixMenuCtor),
BindingFlags.Static | BindingFlags.NonPublic);
var methodKeyDownEvent = typeof(CtrlAltOverride).GetMethod(nameof(PrefixKeyDownEvent),
BindingFlags.Static | BindingFlags.NonPublic);
if (ghMethodMenuCtor == null || ghMethodKeyDownEvent == null)
return false;
harmony.Patch(ghMethodMenuCtor, postfix: new HarmonyMethod(methodRegisterContextMenu));
harmony.Patch(ghMethodKeyDownEvent, prefix: new HarmonyMethod(methodKeyDownEvent));
Instances.DocumentEditor.KeyPreview = true;
Instances.ActiveCanvas.MouseClick += FallbackCleanup;
return true;
}
}
}
Hi,
Thx for the sharing !
It seems to work
Unfortunalety I can’t figure out yet where this code should end up if I want to compile it.
Is it meant to be in a grasshopper plugin ?
Is there an entry point ? Where should it be invoked/registered ?
Regards.
PS: I tried the following and It works. But the “PancakeDev” menu does not appear. Which is fine. But I wonder if I my method is good : FilterAppEvent.cs (4.4 KB)