Review of "AutoTile" version 17.3 (1)

Details Page Preview

AutoTile is a GNOME Shell extension that brings **automatic tiling window management** to GNOME. It is heavily inspired by **[Omarchy](https://omarchy.org) 4.0** and the window layouters of **[Hyprland](https://hyprland.org)**: that is why we forked the excellent **[Tiling Shell](https://github.com/domferr/tilingshell)** project (many thanks to its original author [Domenico Ferraro](https://github.com/domferr)) and extended it, so that GNOME users can enjoy an auto-tiling experience that comes close to **Omarchy Linux** — huge thanks to DHH and the Omarchy developers for their aesthetics, inspiration, philosophy and outstanding code contributions.

Extension Homepage
https://github.com/kylelee/AutoTile

No comments.

FAQ

Files

Note: Binary files aren't shown on the web site. To see all files, please download the extension zipfile.

Shexli (experimental) error 1 warning 5

Shexli found 6 issues that may need reviewer attention.

EGO-X-004 warning

extensions should avoid synchronous file IO in shell code

Shell code should avoid synchronous file IO APIs like `GLib.file_get_contents()` and `Gio.File.load_contents()`.

File Operations

  • translations.js:19
    Gio.File.new_for_path(
            `${extensionPath}/locale/${catalogName}/LC_MESSAGES/${domain}.mo`
          ).load_contents(null)

EGO-C49-003 error

extensions targeting GNOME 49 must not call maximize or unmaximize with Meta.MaximizeFlags

This extension explicitly targets GNOME Shell 49 but still passes `Meta.MaximizeFlags` to `maximize()` or `unmaximize()`.

Meta.Window

  • utils/gnomesupport.js:29
    window.maximize(Meta.MaximizeFlags.BOTH)
  • utils/gnomesupport.js:33
    window.unmaximize(Meta.MaximizeFlags.BOTH)

EGO-L-002 warning

objects created by extension should be destroyed in disable()

Objects assigned in `enable()` are missing matching `.destroy()` calls in `disable()` or its helper methods.

Destroy all objects

  • components/altTab/MultipleWindowsIcon.js:41
        this._label = new St.Label({
          text: t("Tiled windows")
        })
  • components/altTab/tilePreviewWithWindow.js:12
    this._gaps = new Clutter.Margin()
  • components/editor/editorDialog.js:42
        this._layoutsBoxLayout = new St.BoxLayout({
          styleClass: "layouts-box-layout",
          xAlign: Clutter.ActorAlign.CENTER
        })
  • components/windowsSuggestions/suggestionsTilePreview.js:30
    this.layout_manager = new Clutter.BinLayout()
  • indicator/defaultMenu.js:134
        this._container = new St.BoxLayout({
          xAlign: Clutter.ActorAlign.CENTER,
          yAlign: Clutter.ActorAlign.CENTER,
          xExpand: true,
          yExpand: true,
          styleClass: "default-menu-container",
          ...widgetOrientation(true)
        })

EGO-L-005 warning

owned object references should be released in disable()

Owned references that are cleaned up in `disable()` should also be released with `null` or `undefined`.

Destroy all objects

  • components/altTab/MultipleWindowsIcon.js:41
        this._label = new St.Label({
          text: t("Tiled windows")
        })
  • components/altTab/tilePreviewWithWindow.js:12
    this._gaps = new Clutter.Margin()
  • components/editor/editorDialog.js:42
        this._layoutsBoxLayout = new St.BoxLayout({
          styleClass: "layouts-box-layout",
          xAlign: Clutter.ActorAlign.CENTER
        })
  • components/windowBorder/windowBorderManager.js:15
        this._interfaceSettings = new Gio.Settings({
          schema_id: "org.gnome.desktop.interface"
        })
  • components/windowsSuggestions/suggestionsTilePreview.js:30
    this.layout_manager = new Clutter.BinLayout()
  • indicator/defaultMenu.js:134
        this._container = new St.BoxLayout({
          xAlign: Clutter.ActorAlign.CENTER,
          yAlign: Clutter.ActorAlign.CENTER,
          xExpand: true,
          yExpand: true,
          styleClass: "default-menu-container",
          ...widgetOrientation(true)
        })

EGO-L-003 warning

signals connected by extension should be disconnected in disable()

Signals assigned in `enable()` are missing matching disconnect calls in `disable()` or its helper methods.

Disconnect all signals

  • components/editor/editorDialog.js:284
          btn.connect("clicked", () => {
            params.onSelectLayout(btnInd, lay);
            this._makeLegendDialog({
              onClose: params.onClose,
              path: params.path
            });
          })
  • components/editor/editorDialog.js:317
        newLayoutBtn.connect("clicked", () => {
          params.onNewLayout();
          this._makeLegendDialog({
            onClose: params.onClose,
            path: params.path
          });
        })
  • components/editor/layoutEditor.js:158
        editableTile.connect("clicked", (_, clicked_button) => {
          if (clicked_button === St.ButtonMask.ONE)
            this.splitTile(editableTile);
          else if (clicked_button === 3) this.deleteTile(editableTile);
        })
  • components/editor/layoutEditor.js:163
        editableTile.connect("motion-event", (_, event) => {
          const [stageX, stageY] = getEventCoords(event);
          this._hoverWidget.handleMouseMove(
            editableTile,
            stageX - this.x,
            stageY - this.y
          );
          return Clutter.EVENT_PROPAGATE;
        })
  • components/editor/layoutEditor.js:172
        editableTile.connect("notify::hover", () => {
          const [stageX, stageY] = Shell.Global.get().get_pointer();
          this._hoverWidget.handleMouseMove(
            editableTile,
            stageX - this.x,
            stageY - this.y
          );
        })
  • components/raiseTogether/raiseTogetherManager.js:54
        window.connect("unmanaged", () => {
          delete this._raiseId[window.get_id()];
        })
  • components/snapassist/snapAssist.js:69
        this._signals.connect(
          St.ThemeContext.get_for_stage(global.get_stage()),
          "changed",
          () => {
            this._applyStyle();
          }
        )
  • components/windowBorder/windowBorder.js:100
        this._signals.connect(global.display, "restacked", () => {
          this.queue_repaint();
          global.windowGroup.set_child_above_sibling(this, null);
        })
  • components/windowBorder/windowBorderManager.js:49
        this._interfaceSettings.connect(
          "changed::accent-color",
          () => this._border?.updateStyle()
        )
  • components/windowManager/autoTileWindowManager.js:56
        this._signals.connect(
          global.display,
          "window-created",
          (_, window) => {
            window.__ts_cached = new CachedWindowProperties(window, this);
            this._trackWindowSignals(window);
          }
        )
  • indicator/defaultMenu.js:62
          btn.connect(
            "clicked",
            () => !btn.checked && this.emit("selected-layout", lay.id)
          )
  • indicator/defaultMenu.js:349
          row.connect(
            "selected-layout",
            (r, layoutId) => {
              this._indicator.selectLayoutOnClick(
                monitor.index,
                layoutId
              );
            }
          )
  • keybindings.js:29
        this._signals.connect(
          Settings,
          Settings.KEY_ENABLE_MOVE_KEYBINDINGS,
          () => {
            this._setupKeyBindings(extensionSettings);
          }
        )
  • utils/globalState.js:46
        this._signals.connect(
          Settings,
          Settings.KEY_SETTING_LAYOUTS_JSON,
          () => {
            this._layouts = Settings.get_layouts_json();
            this.emit(_GlobalState.SIGNAL_LAYOUTS_CHANGED);
          }
        )

EGO-L-004 warning

main loop sources should be removed in disable()

Main loop sources assigned in `enable()` are missing matching removals in `disable()` or its helper methods.

Remove main loop sources

  • components/tilingsystem/tilingManager.js:1269
            GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
              if (window.minimized || window.maximizedHorizontally || window.maximizedVertically || window.get_transient_for() !== null || window.is_attached_dialog())
                return GLib.SOURCE_REMOVE;
              if (!Settings.ENABLE_AUTO_TIL
  • components/tilingsystem/tilingManager.js:1476
        GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
          try {
            this._executeInsertionPlan(window, plan, anchorWs);
            return GLib.SOURCE_REMOVE;
          } finally {
            _insertionShiftInProgress = false;
          }
        })
  • components/tilingsystem/tilingManager.js:1624
        GLib.timeout_add(GLib.PRIORITY_DEFAULT, 250 * attempt, () => {
          if (window.get_compositor_private() === null)
            return GLib.SOURCE_REMOVE;
          const movedOk = window.get_frame_rect().overlap(tileRect);
          if (!movedOk && attempt < 3) {
            this._auditInsertionMoveLater(
         
  • components/windowBorder/windowBorder.js:161
        this._timeout = setTimeout(() => {
          this._computeBorderRadius(winActor).then(() => this.updateStyle());
          if (this._timeout) clearTimeout(this._timeout);
          this._timeout = void 0;
        }, SMART_BORDER_RADIUS_FIRST_FRAME_DELAY)

All Versions

Version Status
18.0 (2) Unreviewed
17.3 (1) Rejected

Previous Reviews on this Version

kylelee auto- rejected
Auto-rejected because of new version 18.0 (2) was uploaded