Home Download Buy Blog Forum Support

show_quick_panel API

show_quick_panel API

Postby SDemonUA on Sun Dec 30, 2012 11:46 pm

Hello,
I start to work on plugin that must help me with "TODO" in my projects. This is my first plugin and work with python and github. [url="https://github.com/SDemonUA/sublime-text2"]Source[/url].
I faced with leak of documentation :( . I use show_quick_panel to display list of TODOs in file and I'd like to do it like goto_anithing : when you focus list item it scrolls the view to that item.
SDemonUA
 
Posts: 5
Joined: Tue Aug 28, 2012 2:17 pm
Location: Cherkassy, Ukraine

Re: show_quick_panel API

Postby agibsonsw on Mon Dec 31, 2012 12:11 am

Here is some code that I use; it should point you in the right direction.

Code: Select all
def isView(view_id):
    # check that they are in a View, rather than some panel, etc.
    if not view_id: return False
    window = sublime.active_window()
    view = window.active_view() if window != None else None
    return (view is not None and view.id() == view_id)

def showRegion(view, reg):
    view.sel().clear()
    view.sel().add(reg)
    view.show(reg)

class QuickEditsCommand(sublime_plugin.TextCommand):
    # Shows a quick panel to jump to edit lines.
    def run(self, edit):
        self.vid = self.view.id()
        if not isView(self.vid):
            sublime.status_message('Click into the view/tab first.')
            return
        edited = adjustEdits(self.view)
        if not edited:
            sublime.status_message('No edits to list.')
            return
        the_edits = getEditList(self.view, edited)
        if the_edits:
            sublime.active_window().show_quick_panel(the_edits, self.on_chosen)

    def on_chosen(self, index):
        if index == -1: return
        if not isView(self.vid):
            sublime.status_message('You are in a different view.')
            return
        edited = self.view.get_regions("edited_rgns") or []
        for reg in [r for i, r in enumerate(edited) if i == index]:
            showRegion(self.view, reg)
            break
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
agibsonsw
 
Posts: 901
Joined: Fri Jan 27, 2012 9:11 pm

Re: show_quick_panel API

Postby agibsonsw on Mon Dec 31, 2012 12:16 am

I don't know if my getEditList function below is also useful, but it constructs a list (with line numbers, etc.) that is used for display in the quick panel.

Code: Select all
def getEditList(view, edited):
    the_edits = []
    curr_edit = view.get_regions("edited_rgn") or []
    curr_edit = curr_edit[0] if curr_edit else None
    for i, r in enumerate(edited):
        curr_line, _ = view.rowcol(r.begin())
        curr_text = view.substr(r).strip()[:50]
        if not len(curr_text):
            curr_text = view.substr(view.line(r)).strip()[:50] + " (line)"
        if curr_edit and r.intersects(curr_edit):
            display_line = "*%03d %s" % (curr_line + 1, curr_text)
        else:
            display_line = " %03d %s" % (curr_line + 1, curr_text)
        the_edits.append(display_line)
    return the_edits
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
agibsonsw
 
Posts: 901
Joined: Fri Jan 27, 2012 9:11 pm

Re: show_quick_panel API

Postby SDemonUA on Mon Dec 31, 2012 12:41 am

Thanks. But this part I already have. I interested in some onfocus event for show_quick_panel, not only onselect.
SDemonUA
 
Posts: 5
Joined: Tue Aug 28, 2012 2:17 pm
Location: Cherkassy, Ukraine

Re: show_quick_panel API

Postby agibsonsw on Mon Dec 31, 2012 3:13 am

SDemonUA wrote:Thanks. But this part I already have. I interested in some onfocus event for show_quick_panel, not only onselect.


Sorry, I mis-read - must be tired.
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
agibsonsw
 
Posts: 901
Joined: Fri Jan 27, 2012 9:11 pm

Re: show_quick_panel API

Postby agibsonsw on Mon Dec 31, 2012 11:27 pm

I'd like to do it like goto_anithing : when you focus list item it scrolls the view to that item.


You can use the on_modified event to read keystrokes in the quick-panel. You need some way of recognising, though, that the quick-panel is active. show_quick_panel does not return a reference so it might be a little tricky to confirm.

Using the id is probably easiest; it might require a process of elimination though. That is, get all the ids for the views - if the current id is not in this list then deduce that it is the quick panel?! You could do this just before, and just after, showing the quick-panel! My isView function may still be of use.

Added: Actually, just after showing the quick-panel you could just store the id of the current view(?).

I don't know if there is any easier way to confirm that the current view is a quick-panel :?:.

You can use show() or show_at_center() to scroll the view. If you do anything else though you may need to make sure that you focus back on the quick-panel.

Good luck, Andy.
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
agibsonsw
 
Posts: 901
Joined: Fri Jan 27, 2012 9:11 pm

Re: show_quick_panel API

Postby SDemonUA on Tue Jan 01, 2013 1:35 am

Heh, after some digging I found next function useful to determine panel (any panel):
Code: Select all
def isPanel(view):
    (group, index) = view.window().get_view_index(view)
    if group is -1 and index is -1:
        return True
    return False

But I still have no luck to detect focus changes in this panel, up and down arrows does not rises on_modified or on_selection_modified events :(
I start to think that goto_anithing uses some internal mechanisms not available in Python API.

P.S. Thansk for helping, will continue diggin.
SDemonUA
 
Posts: 5
Joined: Tue Aug 28, 2012 2:17 pm
Location: Cherkassy, Ukraine

Re: show_quick_panel API

Postby FichteFoll on Sun Jan 20, 2013 12:22 pm

Here are my thoughts and tests on this (warning: this post is long):

At first I tried to write a function that opens a quick panel and periodically checks until it has opened to write a global flag and id. From this id I could determine in an event listener if the currently "modified" view is the quick panel or just some other view/panel I don't care about. I started using the thread module for this but calling `window.active_view().id()` raised a RuntimeError and told me it cannot be run in a thread other than the main thread. This is the code with `sublime.set_timeout()`:

Code: Select all
import sublime
import sublime_plugin
import sublime_lib



quick_panel_active = False
quick_panel_id = 0


def quick_panel(w, items, callback, flags=0):
    global quick_panel_active
    timeout = 2000

    def find_quick_panel_id(w, current_id, time=0):
        global quick_panel_active, quick_panel_id

        vid = w.active_view().id()
        print("checking panel", vid)
        if vid != current_id:
            quick_panel_active = True
            print("panel found")
            quick_panel_id = vid

        if time >= timeout:
            print("timeout")
            return

        # recall recursively
        wait = 10
        sublime.set_timeout(lambda: find_quick_panel_id(w, current_id, time + wait), wait)

    quick_panel_active = False  # what if there's still a quick paneL active?
    current_id = w.active_view().id()
    print("cid", current_id)

    sublime.set_timeout(lambda: find_quick_panel_id(w, current_id), 0)
    w.show_quick_panel(items, callback, flags)


class TestCommandCommand(sublime_plugin.WindowCommand):
    string = "abcdefghijklmnopqrstuvwxyz"
    choose = [c * 3 for c in string]

    def is_checked(self):
        return True

    def run(self):
        quick_panel(self.window, self.choose, self.on_done)

    def on_done(self, result):
        print("result", result, self.choose[result] if result != -1 else None)


Turned out that `window.active_view()` does not return panels. Code for testing this behaviour with different panels focused (using sublime_lib.WindowAndTextCommand:

Code: Select all
class ActiveViewCommand(sublime_lib.WindowAndTextCommand):
    def run(self, param=None):
        print("window command", self._window_command, "param", param)
        print("  view", self.view, self.view.id())
        print(" aview", self.window.active_view(), self.window.active_view().id())
        print("aaview", sublime.active_window().active_view(), sublime.active_window().active_view().id())


Using an event listener on "on_modified" - which seems to be the only one fired by panels and thus the only method to get a panel's view object - I could get it to work somehow (using sublime_lib.view.get_text):

Code: Select all
import sublime_plugin
import sublime_lib
vlib = sublime_lib.view

last_view_id = 0
quick_panel_waiting = False
quick_panel_active  = False
quick_panel_id = 0


class TestCommandCommand(sublime_lib.WindowAndTextCommand):
    string = "abcdefghijklmnopqrstuvwxyz"
    choose = [c * 3 for c in string]

    def is_checked(self):
        return True

    def run(self, param=None):
        global last_view_id, quick_panel_waiting

        last_view_id = self.view.id()
        quick_panel_waiting = True
        self.window.show_quick_panel(self.choose, self.on_done)

    def on_done(self, result):
        print("result", result, self.choose[result] if result != -1 else None)


class QuickPanelListener(sublime_plugin.EventListener):

    def on_modified(self, view):
        global quick_panel_active, quick_panel_waiting, quick_panel_id

        if quick_panel_waiting and view.id() != last_view_id:
            quick_panel_active  = True
            quick_panel_waiting = False
            quick_panel_id = view.id()

        if quick_panel_active and view.id() != quick_panel_id:
            quick_panel_active = False

        if quick_panel_active:
            print("text in panel", vlib.get_text(view))


However, this does only work if you actually enter something in the quick panel. If you close the panel instantly with "esc" and then enter something in the console it will be detected as the quick panel instead and there is no way to work around this.

I didn't have any success with using various keybindings (note: I used many different values in the "panel" context). The dummy command just printed something in the console.

Code: Select all
[
    { "keys": ["down"], "command": "dummy",
        "context": [
            {"key": "panel", "operand": "console"},
            {"key": "panel_has_focus"}
        ]
    },
    { "keys": ["up"], "command": "dummy",
        "context": [
            { "key": "overlay_visible", "operator": "equal", "operand": true }
        ]
    }
]



Well, this is how I spent my last few hours without accomplishing anything. Furthermore, even after getting either of these attempts to work you would need both of them to combine the input and trim down the list. And even that would not be enough because the panel uses the fuzzy search for text input and the selected item does not change when you alter the text and it still matches the selected item.
We need a new API component, maybe an "on_select" callback for the `show_quick_panel` method, to accomplish the awesome live-update from the goto "overlay".
FichteFoll
 
Posts: 388
Joined: Fri Mar 16, 2012 11:49 pm
Location: Germany

Re: show_quick_panel API

Postby SDemonUA on Thu Jan 31, 2013 12:12 am

Thanks for your trying FitchteFoll. I spent some time digging into keybindigns and find that this is a bug? Below you can see some code from Default (Windows).sublime-keymap file.
Code: Select all
   { "keys": ["escape"], "command": "hide_panel", "args": {"cancel": true},
      "context":
      [
         { "key": "panel_visible", "operator": "equal", "operand": true }
      ]
   },
   { "keys": ["escape"], "command": "hide_overlay", "context":
      [
         { "key": "overlay_visible", "operator": "equal", "operand": true }
      ]
   }

First check : change command into some other and test it.
Result : panel didn't closing, overlay (i.e. "goto anithing" ) works as before.
Second check : open console and write "sublime.log_commands(1)" and test it.
Result : panel logs some "hide_panel" command, overlay logs nothing.

Update : in change notes to ST3 i see solution :)
API: show_quick_panel() accepts an on_highlighted callback
SDemonUA
 
Posts: 5
Joined: Tue Aug 28, 2012 2:17 pm
Location: Cherkassy, Ukraine


Return to Plugin Development

Who is online

Users browsing this forum: No registered users and 4 guests