Chapter 07

ウィンドウを指定位置に指定サイズで持ってくる

AWtnb
AWtnb
2021.12.30に更新

ウィンドウを好きなサイズで決まった位置に動かせるようだけでも QOL がかなり上がるというのが今年の大きな発見でした。

最近の PowerToys の FancyZones でも似たようなことができますが、キー入力で完結するのが好みです。

config.py

    keymap_global["U1-L"] = "LWin-Right"
    keymap_global["U1-H"] = "LWin-Left"

    # 変換キーとMでカーソル移動の各種機能を呼び出す
    keymap_global["U1-M"] = keymap.defineMultiStrokeKeymap("snap window position with key input!")
    keymap_global["U1-M"]["X"] = lambda : keymap.getTopLevelWindow().maximize()

    # ウィンドウのサイズを変更する(すでに指定サイズになっている場合は最大化)
    def set_wnd_rect(rect):
        wnd = keymap.getTopLevelWindow()
        if list(wnd.getRect()) == rect:
            wnd.maximize()
        else:
            if wnd.isMaximized():
                wnd.restore()
                delay()
            wnd.setRect(rect)

    # ウィンドウ配置先の座標ペアを辞書形式で取得する
    def get_rects_to_snap():
        rects = []
        for mntr in get_monitor_areas():
            left, top, right, bottom = mntr
            width_max = right - left
            width_half = int(width_max / 2)
            width_narrower = int(width_max / 3)
            width_wider = width_narrower * 2
            height_max = bottom - top
            height_half = int(height_max / 2)
            height_shorter = int(height_max / 3)
            height_taller = height_shorter * 2

            def _horizontal_rect(rightward=False):
                def __get_rect(width):
                    lx = left
                    if rightward:
                        lx = right - width
                    return [lx, top, lx + width, bottom]
                return {
                    "small": __get_rect(width_narrower),
                    "large": __get_rect(width_wider),
                    "middle": __get_rect(width_half)
                }
            def _vertical_rect(lower=False):
                def __get_rect(height):
                    ty = top
                    if lower:
                        ty = bottom - height
                    return [left, ty, right, ty + height]
                return {
                    "small": __get_rect(height_shorter),
                    "large": __get_rect(height_taller),
                    "middle": __get_rect(height_half)
                }

            rects.append({
                "left": _horizontal_rect(False),
                "right": _horizontal_rect(True),
                "top": _vertical_rect(False),
                "bottom": _vertical_rect(True),
            })
        return rects

    WND_RECTS = get_rects_to_snap()

各モニタについて、上下左右4エリア × 小(1/3)中(1/2)大(2/3)の12通りの情報を辞書形式で取得します。configure() 直下に変数として格納し、この設定内のグローバル変数のような扱いにしています。

組み合わせが多岐にわたるのでクロージャを作成してキーを割り当てるのもひと苦労です。多重ループで割り当ててみました。

config.py
    def snap_wnd(monitor_index=0, position="left", size="middle"):
        rect = None
        if monitor_index < len(WND_RECTS):
            rect = WND_RECTS[monitor_index][position][size]
        def _snap():
            if rect:
                set_wnd_rect(rect)
        return _snap

    for mod_mntr, mntr_idx in {
        "": 0,
        "A-": 1,
    }.items():
        for mod_area, size in {
            "": "middle",
            "S-": "large",
            "C-": "small",
        }.items():
            for key, pos in {
                "H": "left",
                "L": "right",
                "J": "bottom",
                "K": "top",
            }.items():
                keymap_global["U1-M"][mod_mntr+mod_area+key] = snap_wnd(mntr_idx, pos, size)

現在のウィンドウの幅でメインモニタ中央に持ってこれるようにもしてみました。

config.py
    def centerize_wnd():
        wnd = keymap.getTopLevelWindow()
        if wnd.isMaximized():
            return None
        wnd_left, wnd_top, wnd_right, wnd_bottom = wnd.getRect()
        width = wnd_right - wnd_left
        mntr_left, mntr_top, mntr_right, mntr_bottom = get_monitor_areas()[0]
        center = (mntr_right - mntr_left) / 2
        lx = int(center - width/2)
        to_rect = (lx, mntr_top, lx+width, mntr_bottom)
        set_wnd_rect(to_rect)
    keymap_global["U1-M"]["M"] = centerize_wnd

隣のモニタに移動させて最大化することもできます。

config.py
def snap_and_maximize(rightward=False):
        key = "Left"
        if rightward:
            key = "Right"
        def _snap():
            send_keys("LShift-LWin-"+key)
            delay()
            keymap.getTopLevelWindow().maximize()
        return _snap
    keymap_global["LC-U1-L"] = snap_and_maximize(True)
    keymap_global["LC-U1-H"] = snap_and_maximize(False)