gnome-layout-switcher

What I'm thinking is not an extra extension. We already have enough (or even too much), and I don't do javascript/type script. What I was thinking is a standalone gui that simply downloads appropiate extensions (either from repos or from gnome extensions hub where appropriate) and enables and configures them with gsettings commands. Essentially simplified version of this:

  • remove branding and theming parts
    • do not download or set wallpapers or themes
    • instead of Ubuntu layout use manjaro gnome default
  • use repo packages where possible
  • write nice looking gui for it like zorin has

I don't think it would take that much maintenance and there is no reason to think think it would break with every gnome update, because it would not be integrated into gnome shell.

As for the automatic switching between light and dark theme: why write an extension if you can do it with a simple python or shell script?

In general, I try to avoid projects that require a lot of maintenance. So, I think I agree with your advice.

3 Likes

If you find time why not write a program for change layout etc without exotic extension...

1 Like

I started reading through the docs and did some testing. Seems quite possible to do. However, I ran into some unexpected life issues, so I'll probably be finished in late summer or autumn

Don't worry :slightly_smiling_face: take your time ..

Okay, I'm slowly getting a hang creating gtk guis.

Meanwhile zorin seems to have gotten a new hardware distributor. Great! :grinning:

4 Likes

Here is the gui so far:

#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gio

class HeaderBarWindow(Gtk.Window):
        
    def __init__(self):
        Gtk.Window.__init__(self, title="Gnome Layout Switcher")
        self.set_border_width(10)
        self.set_default_size(300, 300)

        hb = Gtk.HeaderBar()
        hb.set_show_close_button(True)
        hb.props.title = "Layout Switcher"
        self.set_titlebar(hb)
        
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        Gtk.StyleContext.add_class(box.get_style_context(), "linked")

        hb.pack_start(box)

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        self.add(vbox)
# Stack settings
        stack = Gtk.Stack()
        stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
        stack.set_transition_duration(300)
        stack.set_hhomogeneous(True)
        stack.set_vhomogeneous(False)

# The layout tab with radio menu
        radiobox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)

        button1 = Gtk.RadioButton.new_with_label_from_widget(None, "Manjaro Layout")
        button1.connect("toggled", self.on_button_toggled, "Manjaro")
        radiobox.pack_start(button1, False, False, 0)

        button2 = Gtk.RadioButton.new_from_widget(button1)
        button2.set_label("Windows Layout")
        button2.connect("toggled", self.on_button_toggled, "Windows")
        radiobox.pack_start(button2, False, False, 0)

        button3 = Gtk.RadioButton.new_from_widget(button1)
        button3.set_label("MacOs Layout")
        button3.connect("toggled", self.on_button_toggled, "MacOs")
        radiobox.pack_start(button3, False, False, 0)
        
        button4 = Gtk.RadioButton.new_from_widget(button1)
        button4.set_label("Gnome Layout")
        button4.connect("toggled", self.on_button_toggled, "Gnome")
        radiobox.pack_start(button4, False, False, 0)
        
        button5 = Gtk.RadioButton.new_from_widget(button1)
        button5.set_label("Ubuntu Layout")
        button5.connect("toggled", self.on_button_toggled, "Ubuntu")
        radiobox.pack_start(button5, False, False, 0)

        stack.add_titled(radiobox, "radiobox", "Layout")
        stack_switcher = Gtk.StackSwitcher()
        stack_switcher.set_stack(stack)

# The theme tab
        label = Gtk.Label()
        label.set_markup("<big>\n\nTheme setings\n\n\n</big>")
        stack.add_titled(label, "label", "Theme")


# A button to apply the settings
        applybutton = Gtk.Button.new_with_label("Apply")
        applybutton.connect("clicked", self.on_click_me_clicked)
# Pack everything to a box
        vbox.pack_start(stack_switcher, True, True, 0)
        vbox.pack_start(stack, True, True, 0)
        vbox.pack_start(applybutton, True, True, 0)

# Placeholder functions for buttons
    def on_button_toggled(self, button, name):
        if button.get_active():
            state = "on"
        else:
            state = "off"
        print("Button", name, "was turned", state)

    def on_click_me_clicked(self, button):
        print("\"Apply\" button was clicked")


# Show the window        
win = HeaderBarWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

Still some basic stuff I should figure out:

  1. add a static height to apply button and stack switcher
  2. Center the stack switcher

Also, less basic:

  1. Add a preview picture beside the radio menu

If anyone who actually knows their gtk/python wants to advise/contribute, I greatly appreciate it :slight_smile:

So far I'm quite happy with the process. I tried using glade to make the gui, but that felt somehow complicated. So instead I'm copy-pasting and slowly comprehending

https://python-gtk-3-tutorial.readthedocs.io/en/latest/index.html

3 Likes

I tried to take a break from forum, but I experienced a critical failure:

#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gio

class HeaderBarWindow(Gtk.Window):
        
    def __init__(self):
        Gtk.Window.__init__(self, title="Gnome Layout Switcher")
        self.set_border_width(10)
        #self.set_default_size(300, 300)

        hb = Gtk.HeaderBar()
        hb.set_show_close_button(True)
        hb.props.title = "Layout Switcher"
        self.set_titlebar(hb)
        
        vbox = Gtk.Grid(row_homogeneous=False,column_homogeneous=False,row_spacing=10)
        self.add(vbox)
# Stack settings
        stack = Gtk.Stack()
        stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
        stack.set_transition_duration(300)
        stack.set_hhomogeneous(False)
        stack.set_vhomogeneous(False)
        stack.set_vexpand(True)

# Radiomenu
        radiobox = Gtk.Grid()
        radiobox.set_hexpand(True)

        button1 = Gtk.RadioButton.new_with_label_from_widget(None, "Manjaro Layout")
        button1.connect("toggled", self.on_button_toggled, "Manjaro")
        radiobox.attach(button1, 1, 0, 1, 1)

        button2 = Gtk.RadioButton.new_from_widget(button1)
        button2.set_label("Windows Layout")
        button2.connect("toggled", self.on_button_toggled, "Windows")
        radiobox.attach(button2, 1, 1, 1, 1)

        button3 = Gtk.RadioButton.new_from_widget(button1)
        button3.set_label("MacOs Layout")
        button3.connect("toggled", self.on_button_toggled, "MacOs")
        radiobox.attach(button3, 1, 2, 1, 1)
        
        button4 = Gtk.RadioButton.new_from_widget(button1)
        button4.set_label("Gnome Layout")
        button4.connect("toggled", self.on_button_toggled, "Gnome")
        radiobox.attach(button4, 1, 3, 1, 1)
        
        button5 = Gtk.RadioButton.new_from_widget(button1)
        button5.set_label("Ubuntu Layout")
        button5.connect("toggled", self.on_button_toggled, "Ubuntu")
        radiobox.attach(button5, 1, 4, 1, 1)

        stack.add_titled(radiobox, "radiobox", "Layout")
        stack_switcher = Gtk.StackSwitcher()
        stack_switcher.set_stack(stack)
        stack_switcher.set_hexpand(True)
        stack_switcher.props.halign = Gtk.Align.CENTER

# The theme tab
        label = Gtk.Label()
        label.set_markup("<big>\n\nTheme setings\n\n\n</big>")
        stack.add_titled(label, "label", "Theme")

# Preview image
        preview = Gtk.Image()
        preview.set_from_file('/home/matti/.bin/preview.png')
        radiobox.attach(preview, 2, 0, 1, 4)

# A button to apply the settings
        applybutton = Gtk.Button.new_with_label("Apply")
        applybutton.connect("clicked", self.on_click_me_clicked)
# Pack everything to a box
        vbox.attach(stack_switcher, 1, 0, 1, 1)
        vbox.attach(stack,  1, 1, 1, 3)
        vbox.attach(applybutton, 1, 4, 1, 1)

# Placeholder functions for buttons
    def on_button_toggled(self, button, name):
        if button.get_active():
            state = "on"
        else:
            state = "off"
        print("Button", name, "was turned", state)

    def on_click_me_clicked(self, button):
        print("\"Apply\" button was clicked")


# Show the window        
win = HeaderBarWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

Here is the code thusfar. I managed my previous goals, to do next:

  1. Update preview picture when the layout is changed
  2. Hook the backend to the buttons
  3. Add content to the theme tab

Project can be contributed to in here:

4 Likes

I'm kinda struggling with getting the preview window to change. I tried simply setting the preview object (Gtk.Image) again from the Preformatted textfunction, but it gives me an error: NameError: name 'radiobox' is not defined. It seems that the objects/variables are not shared betwen the functions __init__(self) and on_button_toggled(self, button, name). I'll need to somehow export the objects between functions or something. Failing that, I could just do what Zorin did and have images for buttons. I think.

Okay, I'm now pretty happy with the current result. It would be even better if the pictores were clickable with their radio buttons, but this should do for now.

Next up, backend

If someone wants to do the preview artwork for the app, I would greatly appreciate it. Zorin does it with abstracts like this, and I think it is a good approach:

They seems to be using transparent background for the previews to match the theme. I wonder how that works with dark themes...

3 Likes

Aaand now we are here.

Moving on to the backend, I'll need to relocate my environment to gnome desktop to test my settings more efficiently. Compared with gnome-layout-manager, I would use a few different shell extensions and mostly stick to the ones in the repos. Also, I would not set the theme with the layout, similar to what Zorin does.

It would be easier for me to write the backend in bash, but I try to stick to single python script instead as a learning experience.

13 Likes

Looks good on Gnome with the adwaita dark theme, keep up the great work!

4 Likes

On the theme tab I'm thinking of following toggles:

  1. dark theme
  2. automatic dark theme
  3. manjaro branding

and drop down menus for

  1. gtk theme
  2. icon theme
  3. shell theme

I'm not sure about these last ones, because the tweak tool already inclueds these options. However, it would feel weird to omit them from the theming section.

Admittedly the whole theme tab is a bit unnecessary, but I really liked the sliding animation so I wanted to include it :sweat_smile:

2 Likes

Okay, so now there is a prelimary backend and the apply button actually changes the enabled shell extensions. But the radiobutton does not actually choose the layout, it always defaults to gnome currently.

Did you start to have that feeling where you are trying to sleep or do other things ... but you keep thinking about how to implement your if/then cuz 'it might work like...' and eventually you just had to pull up a chair ?
(lookin really good by the way)

@Chrysostomus I played a bit with the layout of Manajro and Mac and made some changes. Here is the code:


def set_layout():
    if layout == 'manjaro':
        cmds = ['gsettings set org.gnome.shell enabled-extensions "[\'dash-to-dock@micxgx.gmail.com\', \'user-theme@gnome-shell-extensions.gcampax.github.com\', \'drive-menu@gnome-shell-extensions.gcampax.github.com\', \'appindicatorsupport@rgcjonas.gmail.com\', \'pamac-updates@manjaro.org\']"',
                ['gsettings --schemadir ~/.local/share/gnome-shell/extensions/dash-to-dock@micxgx.gmail.com/schemas/ set org.gnome.shell.extensions.dash-to-dock dock-position LEFT'],
                ['gsettings --schemadir ~/.local/share/gnome-shell/extensions/dash-to-dock@micxgx.gmail.com/schemas/ set org.gnome.shell.extensions.dash-to-dock extend-height true'],
                ['gsettings --schemadir ~/.local/share/gnome-shell/extensions/dash-to-dock@micxgx.gmail.com/schemas/ set org.gnome.shell.extensions.dash-to-dock dock-fixed true'],
                'echo foo']
    elif layout == 'win':
        cmds = ['gsettings set org.gnome.shell enabled-extensions "[\'dash-to-panel@jderose9.github.com\', \'user-theme@gnome-shell-extensions.gcampax.github.com\', \'appindicatorsupport@rgcjonas.gmail.com\', \'pamac-updates@manjaro.org\', \'arc-menu@linxgem33.com\']"',
                'echo foo']
    elif layout == 'mac':
        cmds = ['gsettings set org.gnome.shell enabled-extensions "[\'dash-to-dock@micxgx.gmail.com\', \'user-theme@gnome-shell-extensions.gcampax.github.com\', \'appindicatorsupport@rgcjonas.gmail.com\', \'pamac-updates@manjaro.org\']"',
                ['gsettings --schemadir ~/.local/share/gnome-shell/extensions/dash-to-dock@micxgx.gmail.com/schemas/ set org.gnome.shell.extensions.dash-to-dock dock-position BOTTOM'],
                ['gsettings --schemadir ~/.local/share/gnome-shell/extensions/dash-to-dock@micxgx.gmail.com/schemas/ set org.gnome.shell.extensions.dash-to-dock extend-height false'],
                ['gsettings --schemadir ~/.local/share/gnome-shell/extensions/dash-to-dock@micxgx.gmail.com/schemas/ set org.gnome.shell.extensions.dash-to-dock dock-fixed false'],
                'echo foo']

I should mention the reason to use --schemadir is because i was unable to set Dash to Dock settings via gsettings only, which led me to this bug report:

2 Likes

Thanks! The layouts have been mostly placeholders thusfar, this should save me a good deal of time!

1 Like

I'm glad if I can help a bit with my limited coding knowledge.

̶a̶̶s̶̶ ̶̶f̶̶o̶̶r̶̶ ̶̶t̶̶h̶̶e̶̶ ̶̶l̶̶a̶̶y̶̶o̶̶u̶̶t̶̶s̶̶,̶̶ ̶̶i̶̶s̶̶ ̶̶t̶̶h̶̶e̶̶r̶̶e̶̶ ̶̶a̶̶ ̶̶l̶̶i̶̶s̶̶t̶̶ ̶̶o̶̶r̶̶ ̶̶c̶̶o̶̶n̶̶f̶̶i̶̶g̶̶ ̶̶f̶̶i̶̶l̶̶e̶̶ ̶̶i̶̶ ̶̶c̶̶o̶̶u̶̶l̶̶d̶̶ ̶̶l̶̶o̶̶o̶̶k̶̶ ̶̶a̶̶t̶̶ ̶̶w̶̶h̶̶i̶̶c̶̶h̶̶ ̶̶e̶̶x̶̶t̶̶e̶̶n̶̶s̶̶i̶̶o̶̶n̶̶s̶̶ ̶̶a̶̶r̶̶e̶̶ ̶̶e̶̶n̶̶a̶̶b̶̶l̶̶e̶̶d̶̶ ̶̶o̶̶n̶̶ ̶̶m̶̶a̶̶n̶̶j̶̶a̶̶r̶̶o̶̶ ̶̶b̶̶y̶̶ ̶̶d̶̶e̶̶f̶̶a̶̶u̶̶l̶̶t̶̶ ̶̶a̶̶f̶̶t̶̶e̶̶r̶̶ ̶̶i̶̶n̶̶s̶̶t̶̶a̶̶l̶̶l̶̶?̶

Never mind I fire up a VM :wink:

I made some more changes to the layouts and currently don't know if anything else should be adjusted. Also added Arc Menu to the Manjaro layout as it seems to be activated on a default install.
Here we go:

def set_layout():
    if layout == 'manjaro':
        cmds = ['gsettings set org.gnome.shell enabled-extensions "[\'dash-to-dock@micxgx.gmail.com\', \'user-theme@gnome-shell-extensions.gcampax.github.com\', \'drive-menu@gnome-shell-extensions.gcampax.github.com\', \'appindicatorsupport@rgcjonas.gmail.com\', \'pamac-updates@manjaro.org\', \'arc-menu@linxgem33.com\']"',
               ['gsettings --schemadir ~/.local/share/gnome-shell/extensions/dash-to-dock@micxgx.gmail.com/schemas/ set org.gnome.shell.extensions.dash-to-dock dock-position LEFT'],
               ['gsettings --schemadir ~/.local/share/gnome-shell/extensions/dash-to-dock@micxgx.gmail.com/schemas/ set org.gnome.shell.extensions.dash-to-dock extend-height true'],
               ['gsettings --schemadir ~/.local/share/gnome-shell/extensions/dash-to-dock@micxgx.gmail.com/schemas/ set org.gnome.shell.extensions.dash-to-dock dock-fixed true'],
                'echo foo']
    elif layout == 'win':
        cmds = ['gsettings set org.gnome.shell enabled-extensions "[\'dash-to-panel@jderose9.github.com\', \'user-theme@gnome-shell-extensions.gcampax.github.com\', \'appindicatorsupport@rgcjonas.gmail.com\', \'pamac-updates@manjaro.org\', \'arc-menu@linxgem33.com\']"',
                'echo foo']
    elif layout == 'mac':
        cmds = ['gsettings set org.gnome.shell enabled-extensions "[\'dash-to-dock@micxgx.gmail.com\', \'user-theme@gnome-shell-extensions.gcampax.github.com\', \'appindicatorsupport@rgcjonas.gmail.com\', \'pamac-updates@manjaro.org\']"',
               ['gsettings --schemadir ~/.local/share/gnome-shell/extensions/dash-to-dock@micxgx.gmail.com/schemas/ set org.gnome.shell.extensions.dash-to-dock dock-position BOTTOM'],
               ['gsettings --schemadir ~/.local/share/gnome-shell/extensions/dash-to-dock@micxgx.gmail.com/schemas/ set org.gnome.shell.extensions.dash-to-dock extend-height false'],
               ['gsettings --schemadir ~/.local/share/gnome-shell/extensions/dash-to-dock@micxgx.gmail.com/schemas/ set org.gnome.shell.extensions.dash-to-dock dock-fixed false'],
               ['gsettings --schemadir ~/.local/share/gnome-shell/extensions/dash-to-dock@micxgx.gmail.com/schemas/ set org.gnome.shell.extensions.dash-to-dock intellihide true'],
               ['gsettings --schemadir ~/.local/share/gnome-shell/extensions/dash-to-dock@micxgx.gmail.com/schemas/ set org.gnome.shell.extensions.dash-to-dock intellihide-mode FOCUS_APPLICATION_WINDOWS'],
                'echo foo']
    elif layout == 'gnome':
        cmds = ['gsettings set org.gnome.shell enabled-extensions "[\'pamac-updates@manjaro.org\', \'user-theme@gnome-shell-extensions.gcampax.github.com\']"',
                'echo foo']

Edit: Because i was bored i made a 1 min video how i switch layouts :laughing: https://youtu.be/7_Cvqc7TJUc

2 Likes

Huge thanks @papajoke for help.

Currently:

  1. Images are clickable and there is a fade effect to show which layout is chosen
  2. apply button actually changes the layout

I think that the layout gui is pretty much done for now. Next steps:

  1. perfect the layouts
  2. package the app for testing
  3. start working on the theme tab
7 Likes

I prefer a more python structure: no if else ; more easy to read (and we can also use a extern json)

    def on_click_me_clicked(self, button):
        commands = {
            'manjaro': [
                'gsettings set org.gnome.shell enabled-extensions "[\'dash-to-dock@micxgx.gmail.com\', \'user-theme@gnome-shell-extensions.gcampax.github.com\', \'drive-menu@gnome-shell-extensions.gcampax.github.com\', \'appindicatorsupport@rgcjonas.gmail.com\', \'pamac-updates@manjaro.org\', \'arc-menu@linxgem33.com\']"',
                'gsettings set org.gnome.shell.extensions.dash-to-dock dock-position LEFT',
                'gsettings set org.gnome.shell.extensions.dash-to-dock extend-height true',
                'gsettings set org.gnome.shell.extensions.dash-to-dock dock-fixed true',
                'gsettings set org.gnome.shell.extensions.arc-menu menu-button-text "Custom_Text"',
                'gsettings set org.gnome.shell.extensions.arc-menu custom-menu-button-text " Menu"',
                'gsettings set org.gnome.desktop.wm.preferences button-layout ":minimize,maximize,close"',
            ],
            'win': [
                'gsettings set org.gnome.shell enabled-extensions "[\'dash-to-panel@jderose9.github.com\', \'user-theme@gnome-shell-extensions.gcampax.github.com\', \'appindicatorsupport@rgcjonas.gmail.com\', \'pamac-updates@manjaro.org\', \'arc-menu@linxgem33.com\']"',
                'gsettings --schemadir /usr/share/gnome-shell/extensions/dash-to-panel@jderose9.github.com/schemas set org.gnome.shell.extensions.dash-to-panel show-show-apps-button false',
                'gsettings set org.gnome.shell.extensions.arc-menu menu-button-text "Custom_Text"',
                'gsettings set org.gnome.shell.extensions.arc-menu custom-menu-button-text " "',
                'gsettings set org.gnome.desktop.wm.preferences button-layout ":minimize,maximize,close"',
            ],
            'mac' : [
                'xxx'
            ],
            'gnome' : [
                'one xxx',
                'two xxx',
            ]
        }

        for cmd in commands.get(self.layout, ""):
            subprocess.run(cmd, shell=True)
3 Likes