#Keybinding API Office Hours

1 messages Β· Page 1 of 1 (latest)

azure ocean
young dragon
#

What's the recommended way to implement Ctrl+Click or Shift+Click? I was previously checking _downkeys on the click event listener:

game.keyboard._downKeys.has("Control") || game.keyboard._downKeys.has("Shift");```
Should I just change those to all caps (`CONTROL` and `SHIFT`) and that's it?
azure ocean
# young dragon What's the recommended way to implement Ctrl+Click or Shift+Click? I was previou...

short answer is yes, but _downKeys is deprecated.

longer answer is one I talked with @flint remnant about, which is to register an Action to keep track of the state of something being held, which would allow Users to configure what key that is:

        game.keybindings.register("my-module", "interaction-modifier", {
            name: "Modifier key for dragging and clicking",
            uneditable: [
              {
                key: "SHIFT"
              }
            ],
            onDown: () => { console.log("Shift Down") },
            onUp: () => { console.log("Shift Up") }
          })
young dragon
#

Could I make it editable instead? Since there is no UI for keybindings yet, this will just mean that it can be edited with the set command, right?

azure ocean
#

uneditable can never be unset, but can be added to. editable can be overridden. Both allow a user to bind new bindings to the action

young dragon
#

What's the use case for uneditable in that case?

#

I just added keybinding support (locally) to 4 of my modules and I noticed a few things which were unexpected:
I originally sxpected a modifier field, but, when I found out that it's called modifiers and takes an array, I thought that meant that all of the keys in the array would need to be held in order to fire the keybind. Is there another way to create keybindings which require multiple modifiers to be held simultaneously?
Not sure why, but I expected to see my keybindings at the bottom of the Controls Reference and I think that could be a useful feature.
The SHIFT key doesn't fire it's onUp... (it might actually be the CONTROL key that doesn'tβ€”I'll check in the morning)
Setting a keybinding for the arrow keys, overrides and removes your ability to move around tokens.

flint remnant
#

Shift definitely does fire onUp.

azure ocean
azure ocean
young dragon
# azure ocean if multiple `modifiers` isn't being required, that may be a bug, will check. UI...

Yeah, CONTROL is definitely does not fire an onUp:

game.keybindings.register(Ctg.ID, "rollGroupInitiative", {
    name: "Roll Group Initiative",
    hint: "When pressed down, clicking on any of the initiative rolling buttons in the Combat Tracker will result in a Group Initiative roll.",
    uneditable: [
        {
            key: "SHIFT"
        },
        {
            key: "CONTROL"
        }
    ],
    onDown: () => { console.log("DOWN"); Ctg.groupInitiativeKeybind = true; },
    onUp: () => { console.log("UP"); Ctg.groupInitiativeKeybind = false; },
}));```
young dragon
#

Also, onUp is not firing if the context menu is open while the key is released

azure ocean
young dragon
azure ocean
#

it's possible the context menu eats Keyboard events, in which case there is nothing we can do about it

#

if you run CONFIG.debug.keybindings = true in your console, you can see the system at work

azure ocean
young dragon
azure ocean
#

what is the output of your game.keybindings.activeKeys?

young dragon
azure ocean
young dragon
#

So, there is nothing you can do about it?

azure ocean
#

nope 😦

young dragon
#

Using the browser keydown/keyup event listeners worked...

#

I guess I could register one of those too as a fallback

#

Just for the keyup

azure ocean
#

hmm, we do skip isComposing events, perhaps the context menu counts as that?

#

nope, our handler isn't even getting hit while the context menu is open

young dragon
#

Oh, nevermind. This happens regardless when I do it myself

azure ocean
#

browsers do a fair bit to make sure malicious JS doesn't stop default behaviors users expect

young dragon
#

Yep, but I have found the default context menu very useful for inspecting elements quickly

azure ocean
#

although now that I think about it, perhaps we should also protect keys such as F5, which can be prevented from doing default behavior

azure ocean
young dragon
azure ocean
azure ocean
azure ocean
hoary furnace
azure ocean
#

it is still encouraged to use Keybind Action workflows in cases where it makes sense to allow the User to choose their own Keys to alter behavior - we hope to offer Controller support in the future, and someone may want to modify the drag with right bumper for instance

young dragon
#

Yeah, of course πŸ‘
Thanks for listening to our feedback 😊

young dragon
#

on v9t1 build 233

EDIT: fixed in v9t2

summer beacon
#

for my module (Alternative Rotation) the use case is:

  • while a user is holding Shift, if the user drags a token, it rotates the token instead
  • while a user is holding Shift and Control, if the user drags a token, it rotates the token instead
    I am able to create a single key for either of them - e.g. Shift for rotating works - but I don't know what register parameters I need to use to allow the option of basically an optional Control modifier on top of the Shift modifier.

I tried independently registering Shift and Control, but then if the user is already holding Shift and presses down Control, the key binding is not triggered.
I tried playing around with it but couldn't find a good way to get a trigger when Shift is up, Shift is down, Ctrl is up, Ctrl is down (regardless of whether the other one is down). I could only see a "Shift up", "Shift+Control up", "Control+Shift Down", etc. when I have both of them pressed.

azure ocean
#

You can then check the context in your action to see if control is pressed

ashen bramble
#

why does this code here create a Keybind on C instead of shift+C?

  game.keybindings.register("a11y-chatfinder", "chatfinder", {
    name: "Chatfinder",
    hint: "Puts a cursor in the chat window and activates it.",
    editable: [
      {
        key: "C",
        modifiers: "SHIFT"
      }
    ],
    onDown: async () => { 
      let element = $("#chat-message");
      element.focus();
      await new Promise(resolve => {setTimeout(resolve, 50);});
      element.val("");
    },
    onUp: () => {},
    restricted: false,
    reservedModifiers: [],
    precedence: CONST.KEYBINDING_PRECEDENCE.PRIORITY
  });
```found it. this code works now.
#

also wanna say it's nice working with this. It doesn't break on the things the last lib I worked with did.

#

(or my own very bad code for that matter)

azure ocean
ashen bramble
#

Instead of editable.modifiers I had SHIFT in requireredModifiers.

ashen bramble
#

just to be sure ```js
isAlt(event) {
return game.keyboard.isModifierActive(KeyboardManager.MODIFIER_KEYS.ALT);
return game.keyboard.downKeys.has("ALT");
}

isCtrl(event) {
return game.keyboard.isModifierActive(KeyboardManager.MODIFIER_KEYS.CONTROL);
return game.keyboard.downKeys.has("CONTROL");
}

isShift(event) {
return game.keyboard.isModifierActive(KeyboardManager.MODIFIER_KEYS.SHIFT);
return game.keyboard.downKeys.has("SHIFT");
}```works and I can delete the second lines?

#

and since you wanted to help @azure ocean, this is correct syntax when I want to believe in V9 not breaking the deal any further? json "minimumCoreVersion": "9.236", "compatibleCoreVersion": "9"

ashen bramble
#

okay, TAH fix is pushed πŸ™‚

#

while you are mucking in Keybinding Code: make my latest module obsolete pl0x? Nath said you should ;P

thin oar
#

might be stable might wait til v10

ashen bramble
#

oke. Updated that one too anyway. now to mint those releases and announce them.

summer beacon
#

shift-c for chat?

ashen bramble
#

actually - since I want to have this out at release - I need another look at the modifiers on editable. Are those really an array because I tested a11y-chatfinder with just "SHIFT" and it worked

ashen bramble
azure ocean
ashen bramble
#

it's a "one liner"

azure ocean
#

why do you set val to empty? Is focusing not enough?

ashen bramble
#

I do not know for sure, I was following a guide. I am good at implementing what others already gave me

young dragon
#

I don't think it's necessary. There are other ways to clear the chat (with the keyboard) after focusing on it. This could be confusing and undesirable

summer beacon
#

I'm also in favor of adding some QoL shortcuts for various mouse-based actions, though I would prefer if they were not uneditable

ashen bramble
azure ocean
ashen bramble
azure ocean
summer beacon
azure ocean
#

submitting requests to our GL is the best way to get them considered

ashen bramble
azure ocean
solid fractal
#

Is it still possible to register a keybinding with a modifier key like Shift as only key like this: ```js
uneditable: [
{
key: KeyboardManager.MODIFIER_KEYS.SHIFT
},
]

young dragon
azure ocean
azure ocean
flint remnant
#

@azure ocean I saw the notification about a breaking change in the keybinding on my phone earlier while I was out but can't see it now... do I need to change anything in this, please?

    if (isNewerVersion(game.version, "9.230")){
        game.keybindings.register("fate-core-official", "fcoInteractionModifier", {
            name: "Fate Core Official modifier key for dragging and clicking",
            uneditable: [
              {
                key: "SHIFT"
              }
            ],
            onDown: () => { game.system["fco-shifted"] = true; },
            onUp: () => { game.system["fco-shifted"] = false; }
          })
    }```
vast stratus
#

Your key now needs to be β€œShiftLeft” or β€œShiftRight” or both

azure ocean
#

yep, you'd want to update your isNewerVersion to check 9.236 and use both ShiftLeft and ShiftRight

flint remnant
#

Okay, is that key:["ShiftLeft", ShiftRight"]

#

Or, do I need to register it as two rules?

azure ocean
flint remnant
#

This should work then, thanks:

if (isNewerVersion(game.version, "9.230")){
        if (!isNewerVersion(game.version, "9.235")){
            game.keybindings.register("fate-core-official", "fcoInteractionModifier", {
                name: "Fate Core Official modifier key for dragging and clicking",
                uneditable: [
                  {
                    key: "SHIFT"
                  }
                ],
                onDown: () => { game.system["fco-shifted"] = true; },
                onUp: () => { game.system["fco-shifted"] = false; }
              })
        }

        if (isNewerVersion(game.version, "9.236")){
            game.keybindings.register("fate-core-official", "fcoInteractionModifier", {
                name: "Fate Core Official modifier key for dragging and clicking",
                uneditable: [
                {
                    key: "ShiftLeft"
                },
                {
                     key: "ShiftRight"
                }
                ],
                onDown: () => { game.system["fco-shifted"] = true; },
                onUp: () => { game.system["fco-shifted"] = false; }
              })
        }
    }```
#

Well, after I fix the conditional... I'll reverse those and make the 9.235 an else.

hoary furnace
#

i need to toggle a variable on ALT press, how should i do it now? it seems to no longer be working

  const {SHIFT, CONTROL, ALT} = KeyboardManager.MODIFIER_KEYS;
  game.keybindings.register(SMARTTARGET_MODULE_NAME, "altKey", {
    name: game.i18n.localize("smarttarget.keybindings.altkey"),
    editable: [
      {key: ALT}
    ],
    onDown: () => {game.smartTarget.altModifier = true;},
    onUp: () => {game.smartTarget.altModifier = false;},
});
azure ocean
vast stratus
#

Exact same answer as immediately prior question

azure ocean
#

Control, Alt, and Shift are both modifiers and buttons
In the context of a key, they exist as both Left and Right
In the context of a Modifier, we join them together, so use KeyboardManager.MODIFIER_KEYS

hoary furnace
#

ah whops, my bad

azure ocean
# flint remnant This should work then, thanks: ```js if (isNewerVersion(game.version, "9.230")){...

alternative approach with slightly less copy-paste

if (isNewerVersion(game.version, "9.230")) {
  
  let uneditable = [
    {
      key: "SHIFT"
    }
  ];
  
  if (isNewerVersion(game.version, "9.236")) uneditable = [
    {
      key: "ShiftLeft"
    },
    {
      key: "ShiftRight"
    }
  ];

  game.keybindings.register("fate-core-official", "fcoInteractionModifier", {
    name: "Fate Core Official modifier key for dragging and clicking",
    uneditable: uneditable,
    onDown: () => { game.system["fco-shifted"] = true; },
    onUp: () => { game.system["fco-shifted"] = false; }
  })
}
flint remnant
#

I might switch these to editable now there's a UI for that; is that just as simple as switching to editable [...

flint remnant
#

Oh yeah, that's a neat idea.

hoary furnace
#

sorry for not reading above but i'm kind of speedrunning the 4 broken mods from this change since i don't have holidays but instead crunch at work πŸ˜›

ashen bramble
#

are custom keybinds world settings?

azure ocean
#

but they are stored per-user if that's what you meant

ashen bramble
#

good.

#

I meant the stored per user part πŸ˜„

#

per user or per client actually?

azure ocean
#

per client

#

you can have different keybindings for your laptop or your pc or your hampsterwheel with custom axis inputs hooked into Foundry

ashen bramble
#

finally someone thinks of my setup.

#

🐹 🎑

azure ocean
flint remnant
#

Hmm, is the keybinding ui supposed to look like this or is something going wrong?

flint remnant
#

I thought the caching was fixed. πŸ˜›

ashen bramble
#

but should I have no option to add a keybind to a not-bound action?

flint remnant
#

Aha, thanks for that.

azure ocean
#

For now, use key: "F24" since that's not a real key

flint remnant
#

Okay, hmm. So I changed it to 'editable' and I've tested changing the key binding for my Fate Core Official example, but the new key isn't working.
It seems to be checking for it, but it's not toggling my boolean.

#

It's still responding to SHIFT even though it has been edited to be ctrl.

azure ocean
flint remnant
#

Actually, belay that, I think I have some other conditional checks in my code that's still using the legacy behaviour in certain places.

#

Lol, I picked the one modifier key use to test it where I used this as the code:

 let umr = false;
                    if (event.shiftKey && !game.settings.get("fate-core-official","modifiedRollDefault")) umr = true;
                    if (!event.shiftKey && game.settings.get("fate-core-official","modifiedRollDefault")) umr = true;
                    if (umr){
                        await target.rollModifiedSkill(event.target.id);
                    }
                    else {
                        await target.rollSkill(event.target.id);
                    }```
flint remnant
#

Just checking. When I used leftCtrl to replace the keybinding it doesn't fire the keyUp event, but I suspect that's a known thing?

#

It works when I replace it with v (as a random test) though

#

Hmm. this might be a bug. When I deleted the new keybinding, it returned to SHIFT and that broke it. I had to manually put back in ShiftLeft and ShiftRight.

#

Ahh, I think that's because I should've been checking for isNewerVersion 9.235, as 236 is not a greater version than 236

#

Yup, that was it!

acoustic garden
#

What would be the convention for overwriting default keybindings in a system? I’m using alt as a modifier key in the Cypher System for alternative functions of buttons because that’s the one key that all OSs and browsers have in a similar way. I was using ctrl for a while, but then some browsers and OSs register ctrl-click as a right-click. But since alt also highlights all tokens, that can mean a performance hit when a lot of tokens are on the canvas. So I wrote Free Alt as a module, which re-maps highlighting tokens to H. I was thinking of re-mapping this for the Cypher System using the new API, but would that be considered bad practice, as it might be confusing across different systems? Would it be expected that systems re-bind default functions to make the system-specific experience better?

vast stratus
# acoustic garden What would be the convention for overwriting default keybindings in a system? I’...

This probably would not be advisable. Keybindings are a client-side setting, so your set of mapped keybindings is used for all the worlds you play. This means that the default keybindings in your dnd5e world and the default keybindings in your cypher world should probably be the same, aside from any system-specific bindings that exist or modifications that you (the client) have made yourself.

#

Now that "Highlight Objects" is re-bindable, users can choose if they want to avoid having ALT highlight things while used as a modifier

#

I have not really experienced ALT causing a notable performance issue though, even on scenes with very many tokens

acoustic garden
acoustic garden
young dragon
#

Ok, I'm taking a look at Default Context Menu now for the breaking change:
This is what I had before:

uneditable: [
    { key: "CONTROL" }
],

Would I use KeyboardManager.MODIFIER_KEYS now?

uneditable: [
    { key: KeyboardManager.MODIFIER_KEYS.CONTROL }
],
#

Hmm. It doesn't seem to work

solid fractal
young dragon
#

I have to register two separate keybindings?

young dragon
solid fractal
#

Don't think so, and yeah, something like this is now necessary AFAIK: ```js
uneditable: [
{ key: "ControlLeft" },
{ key: "ControlRight" },
],

young dragon
#

Yeah, that works πŸ‘

vast stratus
rustic citrus
#

did I miss a deprecation warning on game.keyboard.isCtrl() in 08x? based on my testing... i did not

#

thanks to @ashen bramble for the snippet above I was able to unblock myself πŸ‘

ashen bramble
rustic citrus
ashen bramble
#

don't ask me, I am just the mad dog πŸ˜„

rustic citrus
ashen bramble
#

🚍
ironmoose

#

it's true though, cody did this >:-c

young dragon
#

My uneditable keybind is showing up twice

#

I can't add a new keybind and it tells me that there is a max of 4

#

Reset to default doesn't remove the duplicate for some reason

#

How does the precedence thing that Atropos is currently talking about on stream work?

azure ocean
azure ocean
# young dragon --- How does the precedence thing that Atropos is currently talking about on str...

when registering a keybind, you can indicate that the keybind should evaluate as one of the first (PRIORITY), one of the last (DEFERRED), or in the middle (NORMAL) by setting precedence when you register, like so:

    game.keybindings.register("core", "measuredRulerMovement", {
      name: "KEYBINDINGS.MoveAlongMeasuredRuler",
      editable: [
        {key: "Space"}
      ],
      onDown: ClientKeybindings._onMeasuredRulerMovement,
      precedence: CONST.KEYBINDING_PRECEDENCE.PRIORITY,
      reservedModifiers: [CONTROL]
    });
young dragon
azure ocean
ashen bramble
#

Atro said it tries to do stuff until an event has reported that the key press is handled.
how to?

azure ocean
ashen bramble
#

thank.

#

updates will flow in tomorrow, today the bot needs to find a tasty energy outlet πŸ™‚

rustic citrus
#

@azure ocean just pinging here instead: looks like game.keyboard.isCtrl was made static at some point in the v9 cycle?

sick totem
#

Finally getting in to applying the new key binds. Is there any path to making key binds which work in input fields or TinyMCE? I can always match it myself, but that kind of defeats the purpose.

azure ocean
sick totem
azure ocean
#

To do what?

sick totem
#

Insert stuff, w/ Quick Insert πŸ™‚

azure ocean
#

Ahhh

sick totem
#

Not a big deal to hack around it, but it's relatively error prone if you change things later.

azure ocean
#

I do not, in fact, have an encyclopedic knowledge of all authors and their modules haha

sick totem
#

No worries, it's not a very common use case

vast stratus
#

My advice would be to use the core keybinding API to define your action and allow users to register their key of choice via the core interface, but to attach your own event listener to handle keypress events which respond to the key code that the user has bound. This approach would work for both input fields which we skip over and in TinyMCE environments.

azure ocean
#

There's a way to manually invoke the action engine, I will share when I'm actually at my desk instead of on my phone

sick totem
#

My current hack: on document keydown, when game.keyboard?.hasFocus (or in TinyMCE), I have some handling with getKeyboardEventContext, _getMatchingActions and _executeKeybind, which will only fire if the action is one of my own.

vast stratus
#

but that general approach seems like the right direction

azure ocean
sick totem
#

Seems to work fine, thanks for the support!
A few notes;
Setting the hint field on the keybind registration seems to work, but it doesn't localize the string.
The categories in the keybindings window get the same class name as the module/system namespace, which is the main class I've used for my main window. Easy to fix for me, but seems a little conflict prone.

azure ocean
azure ocean
#

oh, I see.

sick totem
#

Let me check again

azure ocean
#

nope, I see

marble mortar
#

Question, if I want to have Ctrl and Alt hotkeys to work at the same time, is there any magic I need to do?

#

It would also seem that game.keyboard.downKeys is updated after the onDown event and before the onUp - I guess this was intended?

young dragon
marble mortar
#

Ah, so I'd have to create a keybinding for all possible combinations, even though I'd like them to work independently?

#

Ie, I don't care if Shift + Ctrl is pressed, only if Ctrl was pressed, and if Shift was pressed, for separate uses

#

So if Shift was held down at the point where I press Ctrl, I would expect CtrlDown to fire

marble mortar
#

Oh, neat, thanks!

marble mortar
#

Okay, last question (I hope), if I wanted to have a distinct keybind for just Shift, and another for Ctrl + Shift (in that order), would that be possible?

azure ocean
ashen bramble
#

how do I control the name here?

#

Thinking of splitting into 3 different parts for one module

#

because I'll make that number go from 58 to 70+ in a moment

marble mortar
azure ocean
azure ocean
ashen bramble
#

okay, Namespace: pf2e-u-is-for-utility, but it shows up as "General"

azure ocean
#

oh well lookie there, we did make sure it was a module πŸ˜†

#

for now, you're tied to your module name, but I can expand that

ashen bramble
#

that it translated into my module name is why I ask πŸ˜„

#

please do, I need it to stay sane.

#

that scroll isn't reasonable, but it's also all one liners that seemed reasonably handy depending on just what spells and skills the party is bringing to the table.

#

(can you ping me when this changes, I like being a step ahead when I can, it feels infinitely better than being one step behind)

#

(also once again - Mad thank you from the little community I do stuff for πŸ˜‰ ❀️ )

ashen bramble
#

that does help, and I constantly find myself scrolling in windows that have a search πŸ™‚

young dragon
azure ocean
#

we considered Optional Modifiers but that was not super clear either

#

they are reserved because you can't set them as the Key (which no longer even applies, but did at the time), but they aren't required

young dragon
#

Ok. I appreciate describing them as optional (maybe that word could be used in the docs). I feel like "reserved" and "optional" are almost antonyms, but I guess it makes sense in that context.

azure ocean
young dragon
#

For sure πŸ‘

azure ocean
#

does this not capture that they are optional when you read it? Modifiers such as [ "CONTROL" ] that can be also pressed when executing this Action. Prevents using one of these modifiers as a Binding.

young dragon
#

Not really

azure ocean
#

πŸ‘

sick totem
#

I found a bug closely related to the keyboard refactor. You can get stuck with a currentMouseManager in the DRAG state, I think it's because the keyup event is ignored when in an input field.

  • Hold Ctrl
  • Click an input field (or focus an input field as the result of an action)
  • Release Ctrl
  • Click the canvas
    EDIT: Well, one of my users found it.
manic shore
#

Shared here again, so it doesn't get lost so easily: when you hold a default Firefox key-combination (like CTRL-F) for some longer time (less than 1 second) then it will still be executed by Firefox even when being "prevented".
Doesn't seem to be the case in Chrome.

#

I ran into a problem with return true vs. event.preventDefault(): When two modules use the same keybinding and both use return true then only one can use the binding, but if one uses event.preventDefault then both can use the same binding. Is this expected?

#

I only want to prevent browser default in my module, not prevent other modules from using the same keys.

vast stratus
#

We tend to feel that one keypress should do one thing, not multiple things. If you handle the keypress we assume that other potential handlers should not. You may have a use case where it makes sense for it to pass through even if you handle it, but this isn’t the assumption of our current design

manic shore
#

Ok, so it is expected behavior. Thanks for the clarification.

gloomy ether
#

I've tried reading over the docs, but I'm still a little confused regarding reserved modifiers.
Could I get an example of when it would be better to do one over the other?

game.keybindings.register("myModule", `myKeybinding`, {
    name: "",
    hint: "",
    editable: [{
        key: `KeyA`,
        modifiers: ["Control"]
    }],
    onDown: () => console.log("Control + A was pressed"),
    restricted: false
});

game.keybindings.register("myModule", `myKeybinding`, {
    name: "",
    hint: "",
    editable: [{
        key: `KeyA`
    }],
    onDown: () => console.log("Control + A was pressed"),
    restricted: false,
    reservedModifiers: ["Control"]
});
vast stratus
#

In the first option, the keybinding will only be activated if CTRL+A is pressed, i.e. CTRL is required in order to activate the handler

#

in the second option CTRL has some ancillary behavior as part of the keybind handler, so the binding will activate if you press A OR it will also activate if you press CTRL+A, but the behavior of the handler may differ depending on whether or not CTRL is pressed

#

an example of the first configuration would be CTRL+C to copy tokens, you must use CTRL+C to copy

#

an example of the second configuration (there isn't an example in the core software, so this one is made up) would be if T would target a token but CTRL+T would target that token AND pan the canvas to center on your target (as a made up example)

gloomy ether
#

great explanation, thanks!

manic shore
#

How does a module implement the second configuration?

vast stratus
#

I don't understand the question, the code enso posted above is valid

manic shore
#

I mean, how does the module handle A vs. CTRL+A in the second case when there is only one onDown?

vast stratus
#

inside the handler function you decide what functionality to implement depending on whether CTRL is also pressed or not

manic shore
#

So the module query the keyevent for CTRL? Is that advantageous versus registering two keybinding (one for A and one for CTRL+A)?

vast stratus
#

We would recommend registering two events when the behaviors are substantially different to the point that the user would want to bind them as different keystrokes completely.

#

We would recommend using one event with an optional modifier in cases where the modifier provides a slight alteration to the behavior

manic shore
#

So the shift modifier for turning +-5 ft to +-10 ft in my module might better be implemented via reservedModifier. I will think about that. Thanks for the explanation.

vast stratus
#

yes, if you set shift as a reserved modifier, you can then check context.isShift inside the handler

#

and toggle whether to do 5ft vs 10ft

manic shore
#

I will think about it. Currently I am using 5 keybindings, 2 for +-5, 2 for +-10, might be better, might be worse.

gloomy ether
#

I think the answer is probably "no" but is there any way to get keybinds to be recognized while the chat message text area is focused?

young dragon
vast stratus
#

this is a bit outside of our intended flow. Normally keybindings wouldn't apply within the context of text input