7

Once upon a time there was the fabulous window manager called "enlightenment".

If you used it, and you hit Alt+Tab then you saw a small list of window titles below each other.

Like this:

  • user@remote-host
  • foo@db-server
  • emacs
  • ...

Or like this:

window-switcher-simple-and-beautiful

I liked it a lot.

I want it back. I mean the feature, not the app.

I am using Ubuntu 18.04

I don't want to see icons of applications like this:

alt-tab-is-useless-at-the-moment

I have up to five terminals open. If I press the windows-key I will see the roughly same image (small version of a big terminal) five times.

window-key-icons

It takes time and mental energy to find the right terminal. And I want to switch with the keyboard only, without using the mouse.

The magic behind the terminal:

xtermset -title foo

I have this the bash script which gets executed if I login via ssh.

This way I can distinguish between several terminals easily.

How to get this feature which worked in the year 1998 back?

(Please don't tell me to install the enlightenment window-manager, this question is about a simple feature, not the app)

User "DK Bose" wanted me to show the output of these commands:

===> wmctrl -m
Name: GNOME Shell
Class: N/A
PID: N/A
Window manager's "showing the desktop" mode: N/A
tguettler@aptguettler:~
===> 


tguettler@aptguettler:~
===> wmctrl -lx
0x0200000a  0 desktop_window.Nautilus  aptguettler Schreibtisch
0x01c00178  0 Pidgin.Pidgin         aptguettler tbz
0x02600010  0 Navigator.Firefox     aptguettler command line - List of window names on ALT-Tab - Ask Ubuntu - Mozilla Firefox
0x02200010  0 Mail.Thunderbird      aptguettler Posteingang - tguettler@tbz-pariv.de (IMAP) - Mozilla Thunderbird
0x04400006  0 gnome-terminal-server.Gnome-terminal  aptguettler foooooo
0x044000ce  0 gnome-terminal-server.Gnome-terminal  aptguettler tguettler@aptguettler

The string "foooooo" was set via xtermset -title foooooo. The title was set in a shell which was running ssh on a remote server.

guettli
  • 1,765

5 Answers5

8

I'm not sure it's advisable to attempt to change the window manager in Ubuntu 18.04. The window manager is integrated into GNOME Shell.

You may find Rofi suitable for the purpose you describe while leaving the system's default intact.

Rofi in window switcher mode

  • Rofi is available in the universe section.

  • It has an installed size of 524 kB and and very few dependencies which you can see by running apt show rofi or by simulating its install using apt install -s rofi.

  • Rofi has several functions but the one of interest here is the window switcher.

Once Rofi is installed:

  • Run rofi -dump-config > ~/.config/rofi/config.rasi to generate a local config file which you can modify to suit your needs.

  • Preview the theme you'd like to use by running rofi-theme-selector. If you want to tweak the theme further, you could copy the theme over from /usr/share/rofi/themes to ~/.local/share/rofi/themes and give it a new name to avoid confusion. I like Pop-Dark and named the local version myPop-Dark. In the images posted in this answer, I've used myPop-Dark.

Usage

To demonstrate Rofi's use as an alternative to the system's Alt+Tab:

  • I opened several windows including five xterm windows.

  • I assigned Ctrl+Win+R as a shortcut keyboard combination to run rofi -show window -theme myPop-Dark

In the image posted above, there are three columns. If you always want only the name of the application and the title of the window, edit ~/.config/rofi/config.rasi to change the commented out window-format line to

window-format: "{n}    {t}";

You can also modify the width, the height, and the location of the rofi window.

To bring the window you want into focus, use the arrow keys or your mouse pointer to highlight the entry and then press Enter.

If you have a really long list of windows, press a key that's unique to the window you want to filter out all other windows. If that's not possible, filter the entries by typing, as in the animation below, f, followed by o to limit the list to windows containing the string "foo".

The animation below illustrates that.

filtering windows

To close a highlighted window, press Shift+Delete.

DK Bose
  • 44,553
6

Ubuntu, including 18.04, is now based on GNOME and some additional features could be made available via GNOME Shell Extensions. Go to the website and search: use "window switcher" as keywords and the relevant extension may be listed in the first page of search result.

This one seems relevant and maintained:

Switcher by dlandau

switcher by dlandau in action

Switch windows or launch applications quickly by typing

Use the configured global hotkey (Super+w by default) to open a list of current windows. Type a part of the name or title of the application window you want to activate and hit enter or click on the item you wish to activate. You can use the arrow keys to navigate among the filtered selection and type several space separated search terms to filter further. Use Esc or click anywhere outside the switcher to cancel.

Use the configured global hotkey (Super+x by default) to open the application launcher. Type a part of the name of the application you want to launch and hit enter. You can use Ctrl+Space or Ctrl+Tab to switch between the switcher and the launcher, or when there are no open windows matching a name but there are apps the mode is switched automatically.

You can customize the look and feel and functionality in the preferences.

Extension Homepage: https://github.com/daniellandau/switcher

Shell version: 3.38 (newest) ... 3.14 (oldest)

To bind the common keyboard shortcut Alt+Tab or Super+Tab with this extension, user may be required to use a workaround. A GitHub user, PHLAK, has submitted this issue #63 on GitHub and also explained the workaround in several comments:

I would like to bind the Switcher to Super + Tab but am unable to. I've also noticed I cannot bind it to Alt + Tab either.


I was able to work around this by setting the value directly with dconf:

dconf write /org/gnome/shell/extensions/switcher/show-switcher "['<Super>tab']"

You can also do the same by using the dconf-config GUI.


You might also have to unbind any pre-configured shortcuts using that key combination. Specifically, "Switch applications" is bound to Super+Tab in Gnome.

You can change this by opening the Settings and navigating to Devices > Keyboard. Then Search for Super+Tab and change or remove this binding.

Disclaimer: The original author did not test this extension because not using a GNOME desktop. This answer simply quoted the most seemingly reliable resources found on the web. The screenshot was redone and optimized (122kB) in GIMP instead, because the original screenshot and the animated image were too large (500kB, 7MB).

TL;DR Go to the GNOME Shell Extensions website and install the extension of choice: Switcher by dlandau. Subject to compatibility with the Shell version.


Answerer's note: Included longer comments that explained how this answer fits to the question. One comment that replied to OP query at the time remained as it is, below this answer.

Ubuntu 18.04 uses GNOME Shell 3.28, so the extension is supposedly compatible -- Feb 14 '19 at 19:32

The described feature is something similar to the traditional smart launcher i.e. Kupfer, GNOME Do, or Launchy: press Alt- or any assigned shortcut, then display a launcher window pop-up (requires to type to find opened windows). In contrast, the Shell extension will display a launcher window pop-up that readily show list of opened windows. The typing or using cursor keys is only required for switching to the target window (the only missing criteria). -- Feb 26 '19 at 13:10

In short: the extension "Switcher by dlandau" will display a simple list of the window titles, but does not switch between windows with the same Alt-Tab. That is the closest you can get on GNOME with Shell extension to this date. -- Feb 26 '19 at 13:24

4

I think you are specifically looking for "Cycling through windows in a list" feature that is available in Window Manager Tweaks->Cycling in XFCE.

ALT-TAB Cycling in a List

enter image description here

4

Textual Alt Tab

A late home-cooked one:

In action

enter image description here

How to set up

The setup exists of two tiny scripts, to be saved into one and the same directory:

script 1

#!/usr/bin/env python3
import gi
gi.require_version("Gtk", "3.0")
gi.require_version('Wnck', '3.0')
from gi.repository import Gtk, Wnck, Gdk
import subprocess

css_data = """
.activestyle {
  background-color: grey;
  color: white;
  border-width: 1px;
  border-radius: 0px;
  border-color: white;
}
.defaultstyle {
  border-width: 0px;
  color: black;
  background-color: white;
}
"""

class AltTabStuff(Gtk.Window):
    def __init__(self):
        # css
        self.provider = Gtk.CssProvider.new()
        self.provider.load_from_data(css_data.encode())       
        Gtk.Window.__init__(
            self, title="AltTab replacement"
        )
        self.curr_index = 0
        self.connect('key-press-event', self.get_key)
        self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
        self.set_decorated(False)
        buttongrid = Gtk.Grid()
        self.add(buttongrid)
        self.connect("delete_event", Gtk.main_quit)

        wins = get_winlist()
        self.buttonindex = 0
        self.buttonsets = []
        index = 0
        for w in wins:
            button = Gtk.Button("\t" + w.get_name())
            button.set_relief(Gtk.ReliefStyle.NONE)
            buttongrid.attach(button, 0, index, 1, 1)
            index = index + 1
            button.connect("clicked", raise_window, w)
            self.buttonsets.append([button, w])
        self.set_focus()
        self.show_all()
        Gtk.main()

    def set_focus(self):
        for b in self.buttonsets:
            button = b[0]
            self.set_style(button, active=False)
        newactive = self.buttonsets[self.buttonindex][0]
        self.set_style(newactive, active=True)
        n_buttons = len(self.buttonsets)
        self.buttonindex = self.buttonindex + 1
        if self.buttonindex >= n_buttons:
            self.buttonindex = 0

    def set_style(self, button, active):
        st_cont = button.get_style_context()
        if active:
            st_cont.add_class("activestyle")
            st_cont.remove_class("defaultstyle")
        else:
            st_cont.remove_class("activestyle")
            st_cont.add_class("defaultstyle")
        Gtk.StyleContext.add_provider(
            st_cont,
            self.provider,
            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION,
        )

    def get_key(self, val1, val2):
        keyname = Gdk.keyval_name(val2.keyval)
        if keyname == "Tab":
            self.set_focus()
        elif keyname == "Alt_L":
            window = self.buttonsets[self.buttonindex-1][1]
            button = self.buttonsets[self.buttonindex-1][0]
            raise_window(button, window)
        elif keyname == "Escape":
            Gtk.main_quit()


def raise_window(button, window):
    subprocess.Popen(["wmctrl", "-ia", str(window.get_xid())])
    Gtk.main_quit()

def check_windowtype(window):
    try:
        return "WNCK_WINDOW_NORMAL" in str(
            window.get_window_type()
        )
    except AttributeError:
        pass

def get_winlist(scr=None):
    """

    """
    if not scr:
        scr = Wnck.Screen.get_default()
        scr.force_update()
    windows = [w for w in scr.get_windows() if check_windowtype(w)]
    return windows


AltTabStuff()

script 2

#!/bin/bash

dr=`dirname $0`
f=$dr'/alttab_runner'

if ! pgrep -f $f
then
$f
else
echo "runs"
fi

Do the following steps:

  1. Make sure both Wnck and wmctrl are installed:

    sudo apt install python3-gi gir1.2-wnck-3.0 wmctrl
    
  2. Save script 1 into an empty file as (exactly) alttab_runner, script 2 as (exactly) alttab_alternative. make both scripts executable

  3. Disable the existing Alt-Tab:

    gsettings set org.gnome.desktop.wm.keybindings switch-applications '[]'
    
  4. Set the shortcut (exactly) Alt-Tab to run script 2:

    /path/to/alttab_alternative
    

Usage

Press Alt + Tab to call the switcher (as in the picture), release Alt and press Tab to cycle through the windows, press Alt again to pick the selected window from the list.

Escape will dismiss (close) the switcher.

Options

If you'd like different colors, you can play with the css in script 1 to set your own styling.

enter image description here enter image description here

To do so, edit this section, where activestyle is obviously the currently selected item:

css_data = """
.activestyle {
  background-color: blue;
  color: white;
  border-width: 1px;
  border-radius: 0px;
  border-color: white;
}
.defaultstyle {
  border-width: 0px;
  color: black;
  background-color: white;
}
"""

See for Gtk css options here on font and buttons.


EDIT

If you'd like to stick to exactly Alt + Tab, in the exact key behaviour as the usual one, use instead of script one:

#!/bin/bash

dr=`dirname $0`
user=$USER
f=$dr'/alttab_runner'
trg='/tmp/'$user'_alttab_trigger'

if ! pgrep -f $f
then
$f
else
echo "runs"
touch $trg
fi

And instead of script 2:

#!/usr/bin/env python3
import gi
gi.require_version("Gtk", "3.0")
gi.require_version('Wnck', '3.0')
from gi.repository import Gtk, Wnck, Gdk
import subprocess
from threading import Thread
import time
import os

trigger = os.path.join("/tmp", os.environ["USER"] + "_alttab_trigger")

css_data = """
.activestyle {
  background-color: grey;
  color: white;
  border-width: 1px;
  border-radius: 0px;
  border-color: white;
}
.defaultstyle {
  border-width: 1px;
  color: black;
  background-color: white;
}
"""

class AltTabStuff(Gtk.Window):
    def __init__(self):
        # apply css
        self.provider = Gtk.CssProvider.new()
        self.provider.load_from_data(css_data.encode())       
        Gtk.Window.__init__(
            self, title="AltTab replacement"
        )
        self.curr_index = 0
        self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
        self.set_decorated(False)
        buttongrid = Gtk.Grid()
        self.add(buttongrid)
        self.connect("delete_event", Gtk.main_quit)

        wins = get_winlist()
        self.buttonindex = 0
        self.buttonsets = []
        index = 0
        for w in wins:
            button = Gtk.Button("\t" + w.get_name())
            button.set_relief(Gtk.ReliefStyle.NONE)
            buttongrid.attach(button, 0, index, 1, 1)
            index = index + 1
            button.connect("clicked", raise_window, w)
            self.buttonsets.append([button, w])
        self.set_focus()

        # thread to watch the trigger file
        self.timer = Thread(target=self.wait)
        self.timer.setDaemon(True)
        self.timer.start()

        self.show_all()
        Gtk.main()

    def set_focus(self):
        # rotate the focus + styling
        for b in self.buttonsets:
            button = b[0]
            self.set_style(button, active=False)

        newactive = self.buttonsets[self.buttonindex][0]
        newselected = self.buttonsets[self.buttonindex][1]
        time.sleep(0.03)
        self.set_style(newactive, active=True)
        n_buttons = len(self.buttonsets)
        self.buttonindex = self.buttonindex + 1
        if self.buttonindex >= n_buttons:
            self.buttonindex = 0
        return newselected

    def wait(self):
        """
        wait loop; see if trigger file pops up, or we need to quit on immediate
        key release
        """
        newfocus = self.buttonsets[0][1]
        while True:
            time.sleep(0.05)
            if not self.key_checker():
                # try/except, in case no windows on workspace
                try:
                    self.activate(str(newfocus.get_xid()))
                except TypeError:
                    pass
                Gtk.main_quit()
            if os.path.exists(trigger):
                os.remove(trigger)
                newfocus = self.set_focus()

    def activate(self, arg1, arg2=None):
        # activate the selected window, close preview window
        w = arg2 or arg1
        subprocess.Popen(["wmctrl", "-ia", w])
        Gtk.main_quit()

    def set_style(self, button, active):
        st_cont = button.get_style_context()
        if active:
            # st_cont.add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION)
            st_cont.add_class("activestyle")
            st_cont.remove_class("defaultstyle")
        else:
            st_cont.remove_class("activestyle")
            # st_cont.remove_class("suggested-action")
            st_cont.add_class("defaultstyle")
        Gtk.StyleContext.add_provider(
            st_cont,
            self.provider,
            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION,
        )


    def key_checker(self):
        # check if keys are in a pressed state
        exclude = ["Button", "Virtual", "pointer"]
        keyboards = [
            k for k in get(["xinput", "--list"]).splitlines()
            if not any([s in k for s in exclude])
        ]
        dev_ids = [[
            s.split("=")[1] for s in k.split() if "id=" in s
        ][0] for k in keyboards]
        pressed = False
        for d in dev_ids:
            if "down" in get(["xinput", "--query-state", d]):
                pressed = True
                break
        return pressed


def get(cmd):
    # just a helper
    try:
        return subprocess.check_output(cmd).decode("utf-8").strip()
    except (subprocess.CalledProcessError, TypeError, UnicodeDecodeError):
        pass


def raise_window(button, window):
    subprocess.Popen(["wmctrl", "-ia", str(window.get_xid())])
    Gtk.main_quit()


def check_windowtype(window):
    try:
        return "WNCK_WINDOW_NORMAL" in str(
            window.get_window_type()
        )
    except AttributeError:
        pass


def get_winlist(scr=None):
    if not scr:
        scr = Wnck.Screen.get_default()
        scr.force_update()
    windows = [w for w in scr.get_windows() if check_windowtype(w)]
    return windows


AltTabStuff()

Setup is exactly as the first version:

  1. Make sure both Wnck and wmctrl are installed:

    sudo apt install python3-gi gir1.2-wnck-3.0 wmctrl
    
  2. Save script 1 into an empty file as (exactly) alttab_runner, script 2 as (exactly) alttab_alternative. make both scripts executable

  3. Disable the existing Alt-Tab:

    gsettings set org.gnome.desktop.wm.keybindings switch-applications '[]'
    
  4. Set the shortcut (exactly) Alt-Tab to run script 2:

    /path/to/alttab_alternative
    
Jacob Vlijm
  • 85,475
0

With alttab (alttab -mk Control_L -vertical -t 1300x30 -i 1x1 -bg '#aaaaab' -frame '#597b8d' -fg '#000000' -font "xft:DejaVu Sans Condensed-8"):

enter image description here

sa7
  • 101