def _maybe_record():
    """In the right context, start recording on every mic, otherwise stop."""
    global _last_transition

    if "user._noise_recorder_context" in scope.get("tag", []):
        # Assume it's a fullscreen video if the window is on the PRIMARY screen,
        # and matches the fullscreen dimensions. This may require the primary
        # screen to have a toolbar to work properly.
        app = ui.active_app()
        window = app.active_window
        should_record = 0 == window.rect.compare_to_rect(ui.main_screen().rect)
    else:
        should_record = False

    # The window dimensions can bounce around during the transitions to & from
    # fullscreen, so deadzones are used for debouncing.
    if should_record:
        if (not recording()
                and time.monotonic() > _last_transition + TRANSITION_DEADZONE):
            _last_transition = time.monotonic()
            noise, existing = noise_with_least_data()
            LOGGER.info(f'Recording noise with the least data: "{noise}", '
                        f"{existing / 60:0.1f} mins exist already.")
            record(noise)
            # TODO: Probably enable a tag here so people can hook behaviour
    elif recording(
    ) and time.monotonic() > _last_transition + TRANSITION_DEADZONE:
        _last_transition = time.monotonic()
        stop()
        # Lambda is used becayse Python thinks `print_total_noise_recorded`
        # isn't callable.
        cron.after("2s", actions.self.print_total_noise_recorded)
Exemple #2
0
 def reset(self, _):
     self.offset_x = self.main_screen.width // 2
     self.offset_y = self.main_screen.height // 2
     self.angle = 0
     self.speed = 0.0
     self.main_screen = ui.main_screen()
     ctrl.cursor_visible(True)
Exemple #3
0
def show_commands(context):
    # what you say is stored as a trigger
    # TODO: switch to list of tuples to simplify paging?
    mapping = []
    for trigger in context.triggers.keys():
        action = context.mapping[context.triggers[trigger]]
        mapping.append((
            trigger,
            format_action(action),
        ))

    keymap = {
        '(show quit | show exit)': lambda x: close_webview(),
        'up': Key('pgup'),
        'down': Key('pgdown'),
    }

    main = ui.main_screen().visible_rect

    # need to account for header and footer / pagination links, hence '-2'
    max_items = int(main.height // (FONT_SIZE + 2 * BORDER_SIZE) - 2)

    if len(mapping) >= max_items:
        # use all visible space
        webview.resize(x=main.x, y=main.y, w=main.width, h=main.height)

        total_pages = int(len(mapping) // max_items)
        if (len(mapping) % max_items > 0):
            total_pages += 1

        pages = []

        for page in range(1, total_pages + 1):
            pages.append(mapping[((page - 1) * max_items):((page) *
                                                           max_items)])

        for idx, items in enumerate(
                pages
        ):  # items = mapping[((page-1)*max_items):((page)*max_items)]
            page = idx + 1
            keymap.update({
                'page ' + str(page):
                create_render_page(context, items, page, total_pages)
            })

        render_page(context, pages[0], 1, total_pages)
    else:
        view_height = (len(mapping) + 2) * FONT_SIZE

        webview.resize(x=main.x,
                       y=(main.height - view_height) / 2,
                       w=main.width,
                       h=view_height)
        webview.render(templates['commands'],
                       context_name=context.name,
                       mapping=mapping)

    webview_context.keymap(keymap)
    webview_context.load()
    webview.show()
Exemple #4
0
 def reset(self, _):
     self.save_last()
     self.offset_x = self.main_screen.x
     self.offset_y = self.main_screen.y
     self.main_screen = ui.main_screen()
     self.width = self.main_screen.width
     self.height = self.main_screen.height
Exemple #5
0
 def reset(self, _):
     self.save_state()
     self.count = 0
     self.offset_x = 0
     self.offset_y = 0
     self.main_screen = ui.main_screen()
     self.width = self.main_screen.width
     self.height = self.main_screen.height
Exemple #6
0
 def __init__(self):
     self.main_screen = ui.main_screen()
     self.offset_x = 0
     self.offset_y = 0
     self.width = self.main_screen.width
     self.height = self.main_screen.height
     self.save_last()
     self.mcanvas = canvas.Canvas.from_screen(self.main_screen)
     self.active = False
Exemple #7
0
def render_page(context, mapping, current_page, total_pages):
    main = ui.main_screen().visible_rect
    webview.render(
        templates["commands"],
        context_name=context.name,
        mapping=mapping,
        current_page=current_page,
        total_pages=total_pages,
    )
Exemple #8
0
 def __init__(self, seconds=4, rate=44100):
     self.width = int(ui.main_screen().width)
     self.chunk = int((seconds * rate) / self.width)
     self.history = []
     self.tmp = []
     self.events = []
     self.lock = threading.Lock()
     self.odd = False
     self.hissing = False
 def resize(self, width: int, height: int):
     if not self.need_resize:
         return
     self.need_resize = False
     screen = ui.main_screen()
     rect = ui.Rect(
         screen.x + (screen.width - width) / 2,
         screen.y + (screen.height - height) / 2,
         width,
         height,
     )
     self.canvas.rect = rect
 def __init__(self):
     """Set up sleep states"""
     self.nosignal = 0  # Number of ticks there have been no signal
     self.sleeping = False
     self.main_screen = ui.main_screen()
     self.size_px = Point2d(self.main_screen.width, self.main_screen.height)
     self.settings = {
         "max_eyeless_ticks": settings.get(
             "user.mouse_sleep_tracker_timeout_frames"
         ),
         "sleep_mode": settings.get("user.mouse_sleep_tracker_sleep_mode"),
         "suspend_screen": settings.get("user.mouse_sleep_tracker_suspend_screen"),
     }
Exemple #11
0
 def __init__(self):
     self.states = []
     self.main_screen = ui.main_screen()
     self.offset_x = 0
     self.offset_y = 0
     self.width = self.main_screen.width
     self.height = self.main_screen.height
     self.states.append(
         (self.offset_x, self.offset_y, self.width, self.height))
     self.mcanvas = canvas.Canvas.from_screen(self.main_screen)
     self.active = False
     self.moving = False
     self.count = 0
Exemple #12
0
def _maybe_record():
    """In the right context, start recording on every mic, otherwise stop."""
    global _last_transition, _original_mic, _gui_text

    # The window dimensions can bounce around during the transitions to & from
    # fullscreen, so deadzones are used for debouncing.
    if (
        "user._noise_recorder_context" in scope.get("tag", [])
        and
        # Assume it's a fullscreen video if the window is on the PRIMARY screen,
        # and matches the fullscreen dimensions. This basically assumes the
        # primary screen has a toolbar.
        ui.active_app().active_window.rect == ui.main_screen().rect
    ):
        if (
            not recording()
            and time.monotonic() > _last_transition + TRANSITION_DEADZONE
        ):
            _last_transition = time.monotonic()
            active_mic = microphone.manager.active_mic()
            _original_mic = active_mic.name if active_mic else None
            print("Disabling mic while recording noises.")
            actions.speech.set_microphone("None")
            with _gui_lock:
                # This can take a while (e.g. on a cold disk drive) so pop a
                # message
                _gui_text = "Scanning noise recordings on disk, this may be slow..."
            gui.show()
            noise, existing = noise_with_least_data()
            LOGGER.info(
                f'Recording noise with the least data: "{noise}", '
                f"{existing / 60:0.1f} mins exist already."
            )
            record(noise)
            context.tags.add("user.recording_noises")
    elif recording() and time.monotonic() > _last_transition + TRANSITION_DEADZONE:
        _last_transition = time.monotonic()
        context.tags.remove("user.recording_noises")
        stop()
        gui.hide()
        with _gui_lock:
            _gui_text = None
        print("Re-enabling microphone.")
        if _original_mic:
            actions.speech.set_microphone(_original_mic)
            _original_mic = None
        else:
            # Shouldn't ever get here but just use this as a fallback
            print('No previous mic found. Switching to "System Default"')
            actions.speech.set_microphone("System Default")
def move_to_ocr(m):
    old_pos = ctrl.mouse_pos()
    start = time.time()
    screen = ui.main_screen()
    factor = 1
    if (int(screen.width), int(screen.height)) == RETINA_SIZE:
        factor = RETINA_FACTOR
    midpoint = None
    bounds = [screen.x, screen.y, screen.width, screen.height]
    which = int(parse_word(m._words[1]))
    row = int(which - 1) // 3
    col = int(which - 1) % 3
    bounds = [
        screen.x + int(col * screen.width // 3),
        screen.y + int(row * screen.height // 3),
        screen.width // 3,
        screen.height // 3,
    ]
    midpoint = (bounds[0] + bounds[2] // 2, bounds[1] + bounds[3] // 2)
    print(which, row, col, bounds, midpoint)
    # noinspection PyProtectedMember
    search = join_words(list(map(parse_word, m.dgnwords[0]._words))).lower().strip()
    ctrl.mouse_move(*midpoint)
    print(f"Starting teleport {which} to {search}")
    hocr = ocr_screen(*bounds, factor=factor)
    print(f"... OCR'd screen: {time.time() - start} seconds.")
    tree = ElementTree.XML(hocr)  # type: list[ElementTree.Element]
    # print(list(tree[1]))
    best_pos = None
    best_distance = screen.width + screen.height
    for span in tree[1].iter():
        # print(span, span.attrib.get("class", ""))
        if span.attrib.get("class", "") == "ocrx_word":
            if search in span.text.lower():
                # title is something like"bbox 72 3366 164 3401; x_wconf 95"
                title = span.attrib["title"]  # type: str
                x, y, side, bottom = [
                    int(i) / (SCALE * factor) for i in title.split(";")[0].split()[1:]
                ]
                candidate = bounds[0] + (x + side) / 2, bounds[1] + (y + bottom) / 2
                dist = distance(candidate, midpoint)
                if dist < best_distance:
                    # print(search, span.text, span.attrib["title"])
                    best_pos = candidate
    if best_pos is not None:
        print(f"... Found match, moving to {best_pos}.  {time.time() - start} seconds.")
        ctrl.mouse_move(best_pos[0], best_pos[1])
        return
    print(f"... No match. {time.time() - start} seconds.")
    ctrl.mouse_move(*old_pos)
Exemple #14
0
 def __init__(self):
     self.main_screen = ui.main_screen()
     self.offset_x = self.main_screen.width // 2
     self.offset_y = self.main_screen.height // 2
     self.angle = 0
     self.speed = 0.0
     self.mcanvas = canvas.Canvas.from_screen(self.main_screen)
     self.active = False
     self.last_draw = time.time()
     self.accel = Acceleration(cd=(.001, 100.0),
                               v=(0.0004, 0.0025),
                               lmb=1000.0,
                               ratio=0.3)
     self.hiss_start = time.time()
     noise.register("noise", self.on_noise)
Exemple #15
0
def show_commands(context):
    # what you say is stored as a trigger
    mapping = []
    for trigger in context.triggers.keys():
        action = context.mapping[context.triggers[trigger]]
        mapping.append((trigger, format_action(action)))

    keymap = {
        "(0 | quit | exit | escape)": lambda x: close_webview(),
        "up": Key("pgup"),
        "down": Key("pgdown"),
    }

    main = ui.main_screen().visible_rect

    # need to account for header and footer / pagination links, hence '-2'
    max_items = int(main.height // (FONT_SIZE + 2 * BORDER_SIZE) - 2)

    if len(mapping) >= max_items:
        total_pages = int(len(mapping) // max_items)
        if len(mapping) % max_items > 0:
            total_pages += 1

        pages = []

        # add elements to each page based on the page index
        for page in range(1, total_pages + 1):
            pages.append(mapping[((page - 1) * max_items):((page) *
                                                           max_items)])

            # create the commands to navigate through pages
        for idx, items in enumerate(pages):
            page = idx + 1
            keymap.update({
                "page " + str(page):
                create_render_page(context, items, page, total_pages)
            })

        render_page(context, pages[0], 1, total_pages)
    else:
        view_height = (len(mapping) + 2) * FONT_SIZE
        webview.render(templates["commands"],
                       context_name=context.name,
                       mapping=mapping)

    webview_context.keymap(keymap)
    webview_context.load()
    webview.show()
Exemple #16
0
    def absolute_position(corner: str):
        """Get the absolute position of a corner.

        :returns tuple[int, int]: the position of this corner.

        """
        screen = ui.main_screen().rect
        if corner == Corner.TOP_LEFT:
            return (0, 0)
        elif corner == Corner.TOP_RIGHT:
            return (screen.width, 0)
        elif corner == Corner.BOTTOM_LEFT:
            return (0, screen.height)
        elif corner == Corner.BOTTOM_RIGHT:
            return (screen.width, screen.height)
        else:
            raise ValueError(f'Invalid corner: "{corner}"')
Exemple #17
0
 def __init__(self):
     self.main_screen = ui.main_screen()
     self.center_x = self.main_screen.x + self.main_screen.width // 2
     self.center_y = self.main_screen.y + self.main_screen.height // 2
     self.offset_x = self.center_x
     self.offset_y = self.center_y
     self.first_hiss = True
     self.hiss_job = None
     self.angle = 0
     self.radius = 15
     self.mcanvas = canvas.Canvas.from_screen(self.main_screen)
     self.active = False
     self.last_draw = time.time()
     self.accel = Acceleration(cd=(0.001, 100.0),
                               v=(0.0004, 0.0025),
                               lmb=1000.0,
                               ratio=0.3)
     noise.register("noise", self.on_noise)
    def draw(self, canvas):
        text = self.history[:]
        if not text or not self.enabled:
            return
        paint = canvas.paint
        paint.filter_quality = paint.FilterQuality.LOW
        paint.textsize = 15
        paint.antialias = True

        # canvas.draw_rect(ui.Rect(x - 50, y - 50, 100, 100))
        x = canvas.x + 20
        y = canvas.y + 50
        text_pad = 0
        rect_pad = 10

        # measure text
        width = 0
        text_top = y
        text_bot = y
        line_spacing = 0
        for line in text:
            _, trect = paint.measure_text(line)
            width = max(width, trect.width)
            line_spacing = max(line_spacing, trect.height)
            text_top = min(text_top, y - trect.height)

        x = canvas.x + ui.main_screen().width - 2 * rect_pad - width

        line_spacing += text_pad
        text_bot = y + (len(text) - 1) * line_spacing
        height = text_bot - text_top

        rect = ui.Rect(x - rect_pad, text_top - 2, width + rect_pad * 2,
                       height + rect_pad + 2)
        paint.color = 'ffffffbb'
        paint.style = paint.Style.FILL
        canvas.draw_round_rect(rect, 10, 10)

        paint.color = '000000'
        paint.style = paint.Style.FILL
        for line in text:
            canvas.draw_text(line, x, y)
            y += line_spacing
Exemple #19
0
    def print_mouse_positions() -> None:
        """Print the mouse position relative to each corner.

        Use to get hard-codable positions.

        """
        mouse_pos = ctrl.mouse_pos()
        print(f"Absolute mouse pos: {mouse_pos}")
        screen = ui.main_screen().rect
        print(f"Main screen:        {screen}")
        for corner in [
            Corner.TOP_LEFT,
            Corner.TOP_RIGHT,
            Corner.BOTTOM_LEFT,
            Corner.BOTTOM_RIGHT,
        ]:
            corner_pos = Corner.absolute_position(corner)
            relative = (mouse_pos[0] - corner_pos[0], mouse_pos[1] - corner_pos[1])
            print(f"Position relative to {corner}: {relative}")
Exemple #20
0
    def __init__(self):
        # tweakable variables
        self.beep = False  # requires windows
        self.two_blinks = (
            True  # depricated - one blink means you will have lots of misclicks
        )
        self.magic = 24  # works on 4c might not be optimized # CHANGME if not clicking accuratley (won't work on 4C speed probably like =35)
        self.scroll_sensitivity = 1  # I like it kinda fast. 6-7 slower.

        # state tracking
        self.second = False
        self.blinking = False
        self.nosignal = 0
        self.sleep = False
        self.right_px, self.right_py = 0, 0
        self.lcurclosed = 0
        self.rcurclosed = 0
        self.signal = 0
        self.left_px, self.left_py = 0, 0
        self.counter = 0
        self.invalid_left = 0
        self.invalid_right = 0
        self.left_closed_count = 0
        self.right_closed_count = 0
        self.expire = 700  # lower if you blink twice a lot
        self.focus_sensitivity = 48  # can go lower on 4C Tobii 5 recomended = 50
        self.MAXSIZE = 120  # if memory starts to be a problem
        self.eye_history = []
        self.main_screen = ui.main_screen()
        self.size_px = Point2d(self.main_screen.width, self.main_screen.height)
        self.get_time = lambda: int(round(time() * 1000))
        self.current_time = 0
        self.first_blink = 0
        self.eyes = True
        self.last_blink = False
        self.right_history = self.momentum_state()
        self.left_history = self.momentum_state()
        self.right_open = 1
        self.left_open = 1
Exemple #21
0
    def start_setup(self, setup_type, mouse_position=None):
        """Starts a setup mode that is used for moving, resizing and other various changes that the user might setup"""
        if (mouse_position is not None):
            self.drag_position = [
                mouse_position[0] - self.limit_x,
                mouse_position[1] - self.limit_y
            ]

        if (setup_type not in self.allowed_setup_options
                and setup_type not in ["", "cancel", "reload"]):
            return

        pos = ctrl.mouse_pos()

        # Persist the user preferences when we end our setup
        if (self.setup_type != "" and not setup_type):
            self.drag_position = []
            rect = self.canvas.rect

            if (self.setup_type == "dimension"):
                self.x = 0
                self.y = 0
                self.width = int(rect.width)
                self.height = int(rect.height)
                self.limit_width = int(rect.width)
                self.limit_height = int(rect.height)
                self.preferences.width = self.limit_width
                self.preferences.height = self.limit_height
                self.preferences.limit_width = self.limit_width
                self.preferences.limit_height = self.limit_height
            elif (self.setup_type == "font_size"):
                self.preferences.font_size = self.font_size

            self.setup_type = setup_type
            self.preferences.mark_changed = True
            self.canvas.pause()
            self.canvas.unregister("draw", self.setup_draw_cycle)
            self.canvas = None

            self.event_dispatch.request_persist_preferences()
        # Cancel every change
        elif setup_type == "cancel":
            self.drag_position = []
            if (self.setup_type != ""):
                self.load({}, False)

                self.setup_type = ""
                if self.canvas:
                    self.canvas.unregister("draw", self.setup_draw_cycle)
                    self.canvas = None

                for canvas_reference in self.canvases:
                    canvas_rect = self.align_region_canvas_rect(
                        canvas_reference["region"])
                    canvas_reference["canvas"].rect = canvas_rect
                    canvas_reference["canvas"].freeze()

        elif setup_type == "reload":
            self.drag_position = []
            self.setup_type = ""
            for canvas_reference in self.canvases:
                canvas_reference["canvas"].freeze()

        # Start the setup by mocking a full screen screen region to place the canvas in
        else:
            main_screen = ui.main_screen()
            region = HudScreenRegion("setup", "Setup mode text", "command_icon", "DD4500", main_screen.rect, \
                Point2d(main_screen.rect.x, main_screen.rect.y))
            region.vertical_centered = True
            canvas_rect = self.align_region_canvas_rect(region)
            self.x = canvas_rect.x
            self.y = canvas_rect.y

            if not self.canvas:
                self.canvas = canvas.Canvas(self.x, self.y, self.limit_width,
                                            self.limit_height)
                self.canvas.register("draw", self.setup_draw_cycle)
            self.canvas.move(self.x, self.y)
            self.canvas.resume()
            super().start_setup(setup_type, mouse_position)
</style>

<h3 id="title">History</h3>
<table>
{% for phrase in phrases %}
<tr><td class="phrase">{{ phrase }}</td></tr>
{% endfor %}
<tr><td><i>{{ hypothesis }}</i></td></tr>
</ul>
"""

if WEBVIEW:
    webview = webview.Webview()
    webview.render(template, phrases=["command"])
    webview.show()
    webview.move(1, ui.main_screen().height)


class History:
    def __init__(self):
        self.visible = True
        self.history = []
        engine.register("post:phrase", self.on_phrase_post)

    def parse_phrase(self, phrase):
        return " ".join(word.split("\\")[0] for word in phrase)

    def on_phrase_post(self, j):
        phrase = self.parse_phrase(j.get("phrase", []))
        cmd = j["cmd"]
        if cmd == "p.end" and phrase:
body {
    width: 200px;
    padding: 0;
    margin: 0;
}
</style>
<h3 id="title">DICTATION ON!</h3>
<br>
<br>
Greedy: {{ greedy }}
<br>
<br>
<br>
"""
webview = Webview()
webview.move(ui.main_screen().width - 250, ui.main_screen().height - 270)


def update_context_and_webview():
    ctx1.reload()
    ctx2.reload()
    ctx3.reload()

    if config.dictation_enabled:
        webview.render(template, greedy=str(config.greedy))
        webview.show()
    else:
        webview.hide()


def toggle_greedy(TRUEFALSE):
Exemple #24
0
 def mouse_center():
     """move the mouse cursor to the center of the currently active window"""
     rect = ui.main_screen().rect
     center = (rect.x + rect.width / 2, rect.y + rect.height / 2)
     ctrl.mouse_move(center)
Exemple #25
0
}
</style>

<table>
{% for phrase in phrases %}
<tr><td class="phrase">{{ phrase }}</td></tr>
{% endfor %}
<tr><td><i>{{ hypothesis }}</i></td></tr>
</ul>
'''

if SHOW:
    webview = webview.Webview()
    webview.render(template, phrases=['waiting...'])
    webview.show()
    webview.move(ui.main_screen().width - 100, ui.main_screen().height)

    class History:
        def __init__(self):
            self.history = []
            engine.register('post:phrase', self.on_phrase_post)

        def parse_phrase(self, phrase):
            return ' '.join(word.split('\\')[0] for word in phrase)

        def on_phrase_post(self, j):
            phrase = self.parse_phrase(j.get('phrase', []))
            cmd = j['cmd']
            if cmd == 'p.end' and phrase:
                self.history.append(phrase)
                self.history = self.history[-hist_len:]
Exemple #26
0
import string
import talon
from talon import voice, ui
from talon.voice import Context
from talon.webview import Webview
from .misc.basic_keys import alpha_alt

# reusable constant for font size (in pixels), to use in calculations
FONT_SIZE = 15
# border spacing, in pixels
BORDER_SIZE = int(FONT_SIZE / 6)

main = ui.main_screen().visible_rect
# need to account for header and footer / pagination links, hence '-2'
MAX_ITEMS = int(main.height // (FONT_SIZE + 2 * BORDER_SIZE) - 2)

ctx = Context("help")
webview_context = Context("helpWebView")


def on_click(data):
    if data["id"] == "close":
        return close_webview()
    elif "page" in data["id"]:
        context, _, page = data["id"].split("-")
        if context == "contexts":
            return render_contexts_help(_, int(page))
        return render_commands_webview(get_context(context), int(page))
    else:
        return render_commands_webview(voice.talon.subs.get(data["id"]))
        r = webview.rect
        new_location = {"x": r.x, "y": r.y}
        webview_location = webview_locations[key]
        if (webview_location["x"] != new_location["x"]
                or webview_location["y"] != new_location["y"]):
            webview_locations[key] = new_location
            config.save_config_json("webview_locations.json",
                                    webview_locations)

    webview = webview.Webview()
    webview.render(template, phrases=["command"])
    webview.show()

    key = screen_key()
    if key not in webview_locations:
        webview_locations[key] = {"x": 1, "y": ui.main_screen().height}
    print("moving webview_locations", webview_locations[key])
    webview.move(webview_locations[key]["x"], webview_locations[key]["y"])

    webview.register("blur", webview_blur)


class History:
    def __init__(self):
        self.visible = True
        self.history = []
        engine.register("post:phrase", self.on_phrase_post)

    def parse_phrase(self, phrase):
        return " ".join(word.split("\\")[0] for word in phrase)
Exemple #28
0
 def __init__(self):
     self.history = []
     engine.register('post:phrase', self.on_phrase_post)
     self.canvas = Canvas.from_screen(ui.main_screen())
     self.canvas.register('draw', self.draw)
Exemple #29
0
                return call_it()

            if debounced._timer is None:
                debounced._timer = threading.Timer(wait - time_since_last_call,
                                                   call_it)
                debounced._timer.start()

        debounced._timer = None
        debounced._last_call = 0

        return debounced

    return decorator


screen = ui.main_screen()
size_px = Point2d(screen.width, screen.height)

transparent = '01'
not_transparent = '40'

regular_size = 7
demo_size = 30

last_click = None


# Shows crazy circles
def smooth_location():

    # Calculate smooth location of point
Exemple #30
0
from talon import Module, actions, imgui, Module, scope, ui

mod = Module()
mod.mode("help_scope", "Mode for showing the scope help gui")

setting_max_length = mod.setting(
    "help_scope_max_length",
    type=int,
    default=50,
)


@imgui.open(x=ui.main_screen().x)
def gui(gui: imgui.GUI):
    gui.text("Scope")
    gui.line()
    gui.spacer()
    gui.text("Modes")
    gui.line()
    for mode in sorted(scope.get("mode")):
        gui.text(mode)
    gui.spacer()
    gui.text("Tags")
    gui.line()
    for tag in sorted(scope.get("tag")):
        gui.text(tag)
    gui.spacer()
    gui.text("Misc")
    gui.line()
    ignore = {"main", "mode", "tag"}
    keys = {*scope.data.keys(), *scope.data["main"].keys()}