Unity有基于文本的窗口切换器吗?

视觉切换器在提供上下文方面非常差劲。例如,浏览器窗口缩略图太小,很难区分,而且经常大部分都是空白的(请参见截图)。有没有一种能显示窗口标题列表的切换器?最好还能智能模糊自动完成(就像https://github.com/ctrlpvim/ctrlp.vimhttps://github.com/junegunn/fzf):-)

你在用什么浏览器?你可以尝试使用CompizConfing设置管理器来查看是否可以更改这个,但很可能是硬编码的*(例如,你必须重新编译它才能更改预览大小...)*。但是你可能可以在其他桌面环境(如Xfce、GNOME 3等)中获得类似/更好的功能。 - Wilf
所有的工作空间?所有的应用程序?按应用程序分类还是以其他特定方式分类? - Jacob Vlijm
@JacobVlijm 可配置的,类似于Unity的行为:当前工作区优先。按字母顺序排序:相似的窗口标题可能属于一起。 - Jean Jordaan
发表了我的答案。很好奇你的想法 :) - Jacob Vlijm
这个也可以(切换窗口和浏览器标签):https://github.com/levioctl/textual-switcher/ - EliranLoi
2个回答

“自制产品”的有趣之处在于你可以根据自己的喜好来制作。可能的缺点是,如果一个项目很有趣,你很容易会有些过于投入其中...
下面的脚本可能就是这种情况:)。虽然我本来想详细解释一下它在“内部”是如何工作的,但这是一个“即用型”的解决方案。尽管我添加了一些注释行,但很难对代码进行简单的内部解释。不过,它似乎接近你所寻找的东西。

它是什么

该脚本是一个纯文本解决方案,用于列出所有打开的“普通”应用程序窗口(并提升选择的窗口),但它具有一些选项:
  1. 窗口名称排序列出窗口:

    使用以下命令运行:

    python3 <脚本> -win
    

    enter image description here

    输入要查找的窗口的首个字符或字符组合,然后按回车将窗口置于前台。

  2. 应用程序排序列出窗口:

    使用以下命令运行:

    python3 <脚本> -app
    

    enter image description here

  3. 工作区排序列出窗口:

    使用以下命令运行:

    python3 <脚本> -ws
    

    enter image description here

如您所见,显示的列有:窗口名称应用程序工作区。预设排序的列始终是第一列。

模糊匹配?

要从列表中选择一个项目,只需键入第一个字符。如果有更多符合输入字符的项目,箭头键将只浏览符合输入字符的项目:

enter image description here

此外:
工作区指示 当前工作区以*标记:例如,如果您看到2*,表示窗口位于工作区2且工作区2是当前工作区。无论您有多少个工作区,这都适用。
窗口大小 选择窗口的大小会自动设置为窗口名称(最长的)和要显示的窗口数量,例如:

enter image description here

或者:

enter image description here

使用说明

设置非常简单:

  1. 这个脚本(绝对)需要 wmctrl

    sudo apt-get install wmctrl
    
  2. 然后将下面的脚本复制到一个空文件中,保存为 list_windows.py

  3. 然后使用以下命令进行测试运行:

    python3 /path/to/list_windows.py -app
    python3 /path/to/list_windows.py -win
    python3 /path/to/list_windows.py -ws
    
  4. 如果一切正常工作,请将一个或多个首选命令添加到一个或多个快捷键中:选择:系统设置 > "键盘" > "快捷方式" > "自定义快捷方式"。点击 "+" 并添加命令

脚本

(仍然是“未经打磨”的代码)

#!/usr/bin/env python3
import subprocess
import socket
import sys

arg = sys.argv[1]
# list (column) header titles and their (data) position in the produced window data list
cols = [["Workspace", -1], ["Application name", -2] , ["Window name", -3]]
# rearrange columns, depending on the chosen option
if arg == "-app":
    cols = [cols[1], cols[2], cols[0]]
elif arg == "-ws":
    cols = [cols[0], cols[2], cols[1]]
elif arg == "-win":
    cols = [cols[2], cols[1], cols[0]]
# extract headers, list positions, to be used in the zenity list
col1 = cols[0][0]; i1 = cols[0][1]
col2 = cols[1][0]; i2 = cols[1][1]
col3 = cols[2][0]; i3 = cols[2][1]
# just a helper function
get = lambda cmd: subprocess.check_output([
    "/bin/bash", "-c", cmd
    ]).decode("utf-8")
# analyse viewport data, to be able to calculate relative/absolute position of windows
# and current viewport
def get_spandata():
    xr = get("xrandr").split(); pos = xr.index("current")
    res = [int(xr[pos+1]), int(xr[pos+3].replace(",", "") )]
    spandata = get("wmctrl -d").split()
    span = [int(n) for n in spandata[3].split("x")]
    cols = int(span[0]/res[0]); rows = int(span[1]/res[1])
    curr_vector = [int(n) for n in spandata[5].split(",")]
    curr_viewport = int((curr_vector[1]/res[1])*cols + (curr_vector[0]/res[0])+1)
    return {"resolution": res, "n_columns": cols, "vector": curr_vector, "current_viewport": curr_viewport}

posdata = get_spandata()
vector = posdata["vector"]; cols = posdata["n_columns"]
res = posdata["resolution"]; currvp = posdata["current_viewport"]
# function to distinguish "normal" windows from other types (like the desktop etc)
def check_window(w_id):
    w_type = get("xprop -id "+w_id)
    if " _NET_WM_WINDOW_TYPE_NORMAL" in w_type:
        return True
    else:
        return False
# split windowdata by machine name
mach_name = socket.gethostname()
wlist = [[l.strip() for l in w.split(mach_name)] for w in get("wmctrl -lpG").splitlines()]
# split first section of window data
for i, w in enumerate(wlist):
    wlist[i][0] = wlist[i][0].split()
# filter only "real" windows
real_wlist = [w for w in wlist if check_window(w[0][0]) == True]
# adding the viewport to the window's data
for w in real_wlist:
    w.append(get("ps -p "+w[0][2]+" -o comm=").strip())
    loc_rel = [int(n) for n in w[0][3:5]]
    loc_abs = [loc_rel[0]+vector[0], loc_rel[1]+vector[1]]
    abs_viewport = int((loc_abs[1]/res[1])*cols + (loc_abs[0]/res[0])+1)
    abs_viewport = str(abs_viewport)+"*" if abs_viewport == currvp else str(abs_viewport)
    w.append(abs_viewport)
# set sorting rules
if arg == "-app":
    real_wlist.sort(key=lambda x: x[-2])
elif arg == "-ws":
    real_wlist.sort(key=lambda x: x[-1])
elif arg == "-win":
    real_wlist.sort(key=lambda x: x[-3])
# calculate width and height of the zenity window:
# height = 140px + 23px per line
h = str(140+(len(real_wlist)*23))
# width = 250px + 8px per character (of the longest window title)
w = str(250+(max([len(w[-3]) for w in real_wlist])*8))
# define the zenity window's content
cmd = "zenity --list --hide-column=4 --print-column=4 --title='Window list' "\
      "--width="+w+" --height="+h+" --column='"+col1+"' --column='"+col2+"' --column='"+col3+\
      "' --column='w_id' "+(" ").join([(" ").join([
          '"'+w[i1]+'"','"'+w[i2]+'"','"'+w[i3]+'"','"'+w[0][0]+'"'
          ]) for w in real_wlist])
# finally, call the window list
try:
    w_id = subprocess.check_output(["/bin/bash", "-c", cmd]).decode("utf-8").split("|")[0]
    subprocess.Popen(["wmctrl", "-ia", w_id])
except subprocess.CalledProcessError:
    pass

令人印象深刻的回答 :) - A.B.
这太棒了 :-) 这是一个很好的提醒,工具就在我们手中。我会试用这个脚本,并且给答案点赞。暂时我还不会接受它,以防其他实现方法出现。 - Jean Jordaan
嗨 @JeanJordaan 只是好奇:它是否按预期工作? - Jacob Vlijm
@Jacob,我喜欢它! - Jean Jordaan
@JacobVlijm,已添加https://github.com/jean/text-based-switcher,我将把它推送到PyPI,并完全归功于你 :-) - Jean Jordaan

初步答案: