Sublime Forum

Execute key binding only if status bar is visible?

#1

I’m trying to turn ctrl+x into a general kill anything/close anything/escape anything command. So far I have created these key bindings:

{ "keys": ["ctrl+x"], "command": "hide_panel", "context": { "key": "panel_visible", "operator": "equal", "operand": true } },
{ "keys": ["ctrl+x"], "command": "hide_overlay", "context": [{ "key": "overlay_visible", "operator": "equal", "operand": true }] },	
{ "keys": ["ctrl+x"], "command": "hide_popup", "context": [{ "key": "popup_visible", "operator": "equal", "operand": true }] },	
{ "keys": ["ctrl+x"], "command": "hide_auto_complete", "context": [{ "key": "auto_complete_visible", "operator": "equal", "operand": true }] },	
{ "keys": ["ctrl+x"], "command": "clear_selections", "context": [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": false }]},
{ "keys": ["ctrl+x"], "command": "single_selection", "context": [{ "key": "num_selections", "operator": "not_equal", "operand": 1 }] },

(For reference, “clear_selections” is not a builtin command, but something I added with help from people here.)

This all works well because “ctrl+x” is captured by the key bindings above in the order shown, e.g., I don’t automatically loose a selection at the same time I close the search panel, etc.

I would like to go further and:

  • have ctrl+x to hide the status bar, if not captured by anything of higher precedence, i.e., anything in the list above
  • have ctrl+x hide the sidebar, if not captured by anything of higher precedence, including the “hide status bar” functionality just mentioned
  • have ctrl+x toggle in and out of distraction-free mode, if not captured anything of higher precedence, including the “hide status bar” and “hide sidebar” functionalities just mentioned

(Basically, ctrl+x hides/closes anything it can, and when nothing is left it to hide it reverts to toggling in and out of distraction-free mode.)

My problem is that while I know how to hide status bar (and sidebar), I don’t know how to hide it only if it is visible, which is key to capturing the ctrl+x command, so that it can propagate to “close sidebar” when the status bar is already hidden, etc.

I looked through the lists of key binding contexts mentioned here but I didn’t see anything mentioning the status bar or the sidebar.

Is there a context for the status bar visibility, or some other device I might use to capture the ctrl+x?

Thanks!

=====

PS: Not that it matters for the purpose of this question, but my final plan for world domination is to have “shift+ctrl+x” reopen the sidebar, status bar in reverse order, should I succeed in this :slight_smile:

0 Likes

#2

As far as I know there’s no built in context for capturing the visibility state of the sidebar and status bar, but you can create one yourself with a plugin:

import sublime
import sublime_plugin


class VisibilityContextListener(sublime_plugin.EventListener):
    def on_query_context(self, view, key, operator, operand, match_all):
        if key == "status_bar_visible":
            lhs = view.window().is_status_bar_visible()
        elif key == "sidebar_visible":
            lhs = view.window().is_sidebar_visible()
        else:
            return None

        rhs = bool(operand)

        if operator == sublime.OP_EQUAL:
            return lhs == rhs
        elif operator == sublime.OP_NOT_EQUAL:
            return lhs != rhs

        return None

With that in place, you can use sidebar_visible and status_bar_visible in your contexts as above to have commands only trigger in the appropriate case:

{
    "keys": ["super+t"],
    "command": "echo", "args": {"sidebar": "visible"},
    "context": [
        { "key": "sidebar_visible", "operator": "equal", "operand": true },
    ],
},

{
    "keys": ["super+shift+t"],
    "command": "echo", "args": {"status_bar": "visible"},
    "context": [
        { "key": "status_bar_visible", "operator": "equal", "operand": true },
    ],
},

For detecting whether or not you’re in distraction free mode, the only way I know of would be to apply a custom setting in your distraction free settings, such as the following:

{
    "is_distraction_free": true
}

As Sublime moves in and out of distraction free mode it modifies the settings accordingly, so every view will have the setting set to true when you’re in distraction free mode or not have the setting at all if you’re not.

With that in place you can use the built in setting context to trigger an action based on its state:

{
    "keys": ["super+ctrl+t"],
    "command": "echo", "args": {"is_distraction_free": "yes"},
    "context": [
        { "key": "setting.is_distraction_free", "operator": "equal", "operand": true },
    ],
},
1 Like

#3

Thank you.

It worked.

As for detecting distraction-free mode, I actually don’t need this since distraction-free mode is the last sate on my chain. (That is, when not captured by anything else, “ctrl+x” defaults to toggling in and out of distraction-free mode, and it doesn’t actually need to know if it’s distraction-free mode in order to do that.)

One bad surprise I had along the way is that the key bindings are after all not captured in first-come-first-serve basis according to the order I put them in my user keybindings file, as I thought they were. For example, the distraction-free toggle was being captured before the auto-complete escape, though it appeared later in the file. So I had to neuter the distraction-free toggle by adding the relevant contexts checks, etc.

For reference, here is what my final key bindings look like:

{ "keys": ["ctrl+x"], "command": "hide_panel", "context": 
	[{"key": "panel_visible", "operator": "equal", "operand": true},
]},
{ "keys": ["ctrl+x"], "command": "hide_overlay", "context":
	[{ "key": "overlay_visible", "operator": "equal", "operand": true},
]},	
{ "keys": ["ctrl+x"], "command": "hide_popup", "context":
	[{ "key": "popup_visible", "operator": "equal", "operand": true},
]},	
{ "keys": ["ctrl+x"], "command": "hide_auto_complete", "context": 
	[{"key": "auto_complete_visible", "operator": "equal", "operand": true},
]},	
{ "keys": ["ctrl+x"], "command": "clear_selections", "context":
	[{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": false},
]},
{ "keys": ["ctrl+x"], "command": "single_selection", "context": 
	[{ "key": "num_selections", "operator": "not_equal", "operand": 1},
]},
{ "keys": ["ctrl+x"], "command": "hide_status_bar", "context": 
	[{ "key": "status_bar_visible", "operator": "equal", "operand": true},
]},
{ "keys": ["ctrl+x"], "command": "hide_sidebar", "context": 
	[{ "key": "status_bar_visible", "operator": "equal", "operand": false},
	 { "key": "sidebar_visible", "operator": "equal", "operand": true},
]},
{ "keys": ["ctrl+x"], "command": "toggle_distraction_free", "context":
	[{ "key": "auto_complete_visible", "operator": "equal", "operand": false },
	 { "key": "overlay_visible", "operator": "equal", "operand": false },
	 { "key": "popup_visible", "operator": "equal", "operand": false },
	 { "key": "panel_visible", "operator": "equal", "operand": false },
	 { "key": "status_bar_visible", "operator": "equal", "operand": false },
	 { "key": "sidebar_visible", "operator": "equal", "operand": false },
]},
{ "keys": ["ctrl+shift+x"], "command": "show_status_bar", "context": 
	[{ "key": "sidebar_visible", "operator": "equal", "operand": true},
	 { "key": "status_bar_visible", "operator": "equal", "operand": false},
]},
{ "keys": ["ctrl+shift+x"], "command": "show_sidebar", "context": 
	[{ "key": "sidebar_visible", "operator": "equal", "operand": false},
]},

And here is the custom code gone into ~/Library/Application Support/Sublime\ Text 3/Packages/User/my_own_stuff.py (including your visibility checker):

class VisibilityContextListener(sublime_plugin.EventListener):
    def on_query_context(self, view, key, operator, operand, match_all):
        if key == "status_bar_visible":
            lhs = view.window().is_status_bar_visible()
        elif key == "sidebar_visible":
            lhs = view.window().is_sidebar_visible()
        else:
            return None
        rhs = bool(operand)
        if operator == sublime.OP_EQUAL:
            return lhs == rhs
        elif operator == sublime.OP_NOT_EQUAL:
            return lhs != rhs
        return None


class ClearSelectionsCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        sel = self.view.sel()
        if sel:
            carets = [s.b for s in sel]
            sel.clear()
            for caret in carets:
                sel.add(caret)


class HideStatusBarCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        self.view.window().set_status_bar_visible(False)


class HideSidebarCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        self.view.window().set_sidebar_visible(False)


class ShowStatusBarCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        self.view.window().set_status_bar_visible(True)


class ShowSidebarCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        self.view.window().set_sidebar_visible(True)

Thanks again! :cocktail:

0 Likes