configuring workspacer

use workspacer like a pro!

Workspacer is configured with the C# programming language. The expressiveness of C# allows you to be hyper-specific in terms of describing your desired behavior. This configuration documentation assumes that you have already created the example config in the correct folder, if you haven’t, check the quick-start guide.

I don’t know C#, or how to code, should I use workspacer?

Workspacer is intended to be on the extreme end of the power user spectrum. You can use workspacer without knowing how to code, but advanced configuration will be challenging if you need to deviate outside of the examples and configurations of other users.

How do I define custom workspaces?

Workspaces are created by calling the appropriate function on the IWorkspaceContainer you are using. In the default configuration, that is CreateWorkspace on the context.WorkspaceContainer object. For example:

context.WorkspaceContainer.CreateWorkspaces("more", "than", "one");

Layout Engines

Layout engines define the way windows get arranged on each workspace. There are a number of different ways to lay out windows and you can swap between them dynamically. By default, workspacer loads two engines to switch between, the FullLayoutEngine and the TallLayoutEngine, however you can change the set of default layout engines via:

context.DefaultLayouts = () => new ILayoutEngine[] { new FullLayoutEngine() };

You can even manually specify the layout engines on a per-workspace basis, by passing the different layout engines to the CreateWorkspace (or equivalent) function:

context.WorkspaceContainer.CreateWorkspace("layouts!", new FullLayoutEngine(), new TallLayoutEngine());

Existing Layout Engines

In development

Filters & Routes

By default, workspacer will ignore certain system windows, such as Task Manager and workspacer itself, and will route windows to the focused workspace when they are opened. This is completely customizable via IWindowRouter.

The default configuration provides the standard set of ignore/routing rules, but more can be added via calls to AddFilter and AddRoute.

# custom filter
context.WindowRouter.AddFilter((window) => !window.Title.Contains("my fun application"));
# custom route
context.WindowRouter.AddRoute((window) => window.Title.Contains("Google Chrome") ? context.WorkspaceContainer["web"] : null);

The AddFilter call will ensure that any window title containing the text "my fun application" will be ignored by workspacer. A true return value will allow the window to be managed, while a false value will force workspacer to ignore the window. This is recommended for full-screen applications such as videogames which may have conflicts with workspacer that could lead to unwanted crashes or false detections from an anti-cheat system due to workspacer trying to manage it.

The AddRoute call will ensure that any window title containing the text “Google Chrome” will be automatically placed in the “web” workspace. A null return value will signal to workspacer that the next route should be checked, while returning an actual workspace will ensure that the workspace manager will place the window in the returned workspace.


Workspacer offers additional functionality beyond just tiling your windows through Plugins. This is a way for a developer to ship functionality as a DLL that taps into workspacer without requiring massive amounts of extra code in your config file. Supported plugins include:

Most plugins can be configured using either an Action to change the default configuration, or by passing a configuration directly. For instance, the menu bar plugin may be configured in either of the following two ways:

context.AddBar(new BarPluginConfig()
        FontName = "JetBrainsMono NF",

context.AddBar(context =>
        context.FontName = "JetBrainsMono NF";

In the examples below, the top method is used, as it explicitly states the type of the configuration (in this case BarPluginConfig).

Like other tiling window managers workspacer includes a status/menu bar which shows relevant information to improve your workflow, for example: showing your workspaces, your battery level and the time. It can also have additional features provided through custom widgets by creating a class which inherits from BarWidgetBase.

The bar can be installed like this:

context.AddBar(new BarPluginConfig());

The default workspacer config will do this for you automatically, so the only thing you will likely need to change is the set of widgets installed via the LeftWidgets and RightWidgets properties on the config optional parameter.

An example of a bar, which implements the TimeWidget with a custom time format and the BatteryWidget is shown below:

context.AddBar(new BarPluginConfig()
        BarTitle = "workspacer.Bar",
        FontSize = 14,
        FontName = "JetBrainsMono NF",
        RightWidgets = () => new IBarWidget[] { new TimeWidget(1000,"hh:mm"), new BatteryWidget() },

All widgets output a string and share common properties as defined by the IBarWidgetPart interface.

public interface IBarWidgetPart
        string Text { get; }
        Color ForegroundColor { get; }
        Color BackgroundColor { get; }
        Action PartClicked { get; }
        string FontName { get; }

Widgets support custom on-click functions and callbacks through the PartClicked Action. Icon fonts are also supported and can be set for individual widgets but require system wide installation of the font and there may be issues with .OTF files, so TTFs are recommended. To add icons to a widget you must use its unicode value. For example if you wanted to add this monitor icon from FontAwesome you would have to write "\uf108".

The Action Menu

The action menu is implemented as a plugin, (see the above Plugins section). The action menu can be installed like this:

var actionMenu = context.AddActionMenu();

actionMenu.DefaultMenu.AddMenu("do a thing", () => DoACoolThing());
actionMenu.DefaultMenu.AddMenu("open a menu", () => CreateAMenu(container, actionMenu));
actionMenu.DefaultMenu.AddFreeForm("write to console", (s) => Console.WriteLine(s));

Note that DefaultMenu contains a useful set of default items. If you don’t want these defaults, or you want to create a menu for nesting, you can use the Clear method. You can nest these menus as much as desired, so any set of menus can be created.


Gaps are supported through IConfigContext::AddLayoutProxy which is also used to recalculate window positions when the menu bar is present.

They can be implemented like this:

#r "C:\Program Files\workspacer\plugins\workspacer.Gap\workspacer.Gap.dll"

using workspacer.Gap;

var gap = 20;
    new GapPluginConfig()
        InnerGap = gap,
        OuterGap = gap / 2,
        Delta = gap / 2,

Gaps can be also adjusted on-the-fly by binding the relevant functions (e.g. IncrementOuterGap, DecrementInnerGap) to keybinds. For more details have a look at an example from the user snippets

Title Bar

This adds the ability to remove the title bar from specific or all windows. This is useful for applications that have a title bar which is unncessary for the purpose of the window when Workspacer is in use.

By default, the title bar and sizing border are shown on all windows. To update the defaults:

#r "C:\Program Files\workspacer\plugins\workspacer.TitleBar\workspacer.TitleBar.dll"

using workspacer.TitleBar;

var titleBarPluginConfig = new TitleBarPluginConfig(new TitleBarStyle(showTitleBar: false, showSizingBorder: false));

The available styles can be seen below:

TitleBar styles

To customize the styling for specific windows:

var titleBarPluginConfig = new TitleBarPluginConfig();
titleBarPluginConfig.SetWindowProcessName("Notepad", new TitleBarStyle(showTitleBar: false, showSizingBorder: false));

Setting the filters can be done using the TitleBarPluginConfig methods:

public void SetWindowClass(string windowClass, TitleBarStyle style);
public void SetWindowProcessName(string processName, TitleBarStyle style);
public void SetWindowTitle(string title, TitleBarStyle style);
public void SetWindowTitleMAtch(string match, TitleBarStyle style);

How do I register custom keybindings?

Workspacer subscribes to the default set of key bindings automatically for you. If you want to add additional key bindings, or override an existing one, you can do the following:

Define the mod key. Check KeyModifiers.cs to see all available modifiers. KeyModifiers.Alt is the default mod key. If you want to use multiple modifier keys (e.g. Left Alt + Left Control), use KeyModifiers.LAlt | KeyModifiers.LCtrl.

KeyModifiers mod = KeyModifiers.Alt;

Add a key binding using context.Keybinds.Subscribe. See Keys.cs for all the available key names.

context.Keybinds.Subscribe(mod, Keys.Y, () => Console.WriteLine("Y was pressed"))

If you want to remove a keybinding that already exists, you can unsubscribe from it:

context.Keybinds.Unsubscribe(mod, Keys.Y);

Finally, you can remove all the default keybindings via:


How do I minimize windows?

By default, context.CanMinimizeWindows = false. To enable the minimizing of windows, set:

context.CanMinimizeWindows = true;

How do I change .workspacer configuration folder location?

By default, the .workspacer is located at your userprofile folder, but we can set a UserEnvironmentVariable called “WORKSPACER_CONFIG” to change this location. If the UserEnvironmentVariable exists it will create a folder inside the path that the variable is referencing in and it will use the configuration files inside a %WORKSPACER_CONFIG%/.config/workspacer folder. It will create this path if it doesn’t exist.

How can I troubleshoot Visual Studio Code type checking?

Visual Studio Code uses OmniSharp to provide intellisense and syntax interpolation of C# script files such as your workspacer.config.csx - this should work provided you have the C# extension installed.

However, issues with completion or problems indicated for valid code arise when OmniSharp finds multiple .NET SDKs installed. Each framework is (.NET Framework, .NET Core, .NET etc) a candidate for the backend it uses to understand your .csx file.

One way to confirm this is by viewing OmniSharp’s output in Output > OmniSharp Log after loading workspace.config.csx. For example:

[info]: OmniSharp.Script.ScriptContextProvider
        Searching for compilation dependencies with the fallback framework of 'net461'.

OmniSharp will display the default framework it has chosen which in this case is .NET Framework 4.6.1 on the system instead of the correct .NET. This framework does not understand the lambda expressions we use in config, so the following problem is found for the default template (workspacer.config.template.csx):

Cannot convert lambda expression to type 'Action<IConfigContext>' because it is not a delegate type [workspacer.config.csx] csharp(CS1660)


In the case you don’t require multiple .NET runtimes on your machine, simply uninstall SDKs so that the only SDK installed matches the latest one targeted by workspacer.

Reload VS Code and confirm Output > OmniSharp Log displays the right target framework and no false problems appear.

In the case that multiple frameworks are required, there is also the option of specifying which one OmniSharp should use:

  1. Create an omnisharp.json file in %USERS%/.workspacer (your workspacer.config.csx should also live here)

  2. Add this sample config for .NET 5 (which is targeted at time of writing) to the file:

        "script": {
            "enabled": true,
            "defaultTargetFramework": "net7.0",
            "enableScriptNuGetReferences": true
  3. Reload VS Code and confirm Output > OmniSharp Log displays the right target framework and no false problems appear.