diff --git a/README.md b/README.md index d030ef3..341f74f 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,18 @@ master.mez is meant to be part of the default config of any [Mezzaluna](https://github.com/MezzalunaWM/Mezzaluna) install. It provides basic window management in the master and stack style, simmilar to default dwm. As of right now, since Mezzaluna is in early development, this plugin does a little more than just managing windows. To be fair though, this is more for seeing how a plugin might work and seeing what would be required of plugin developers, and what they have the freedom to customize. There is **NO WAY** to officially install this right now sooooooo, maybe just copy or simlink files where they need to go. Sorry lol. + +## Configuration + +How configuration works exactly is still up for debate as of now. Peronally I don't think that things like binds for spawning windows are necessary for a window manager plugin, so those are left for the user to still define. But here is what configuring master.mez would hopefully look like. + +Also don't know if constructs like workspaces or tags should really be the responsibility of the window manager plugin either, but those will stay for now. + +```lua +local master = requre("master") + +master.setup({ + master_ratio = 0.5, + tag_count = 5 +}) +``` diff --git a/lua/master/builtins.lua b/lua/master/builtins.lua index 731b563..560de36 100644 --- a/lua/master/builtins.lua +++ b/lua/master/builtins.lua @@ -3,18 +3,6 @@ local M = {} -local spawn_terminal = function() - mez.api.spawn("wmenu-run") -end - -local spawn_run_launcher = function() - mez.api.spawn("wmneu-run") -end - -local spawn_background = function() - mez.api.spawn("swaybg -i ~/Images/wallpapers/void/gruv_void.png") -end - local close_focused = function () mez.view.close(0) end @@ -23,6 +11,4 @@ local close_compositor = function () mez.api.exit() end -local - return M diff --git a/lua/master/init.lua b/lua/master/init.lua index b11c6f8..250fc83 100644 --- a/lua/master/init.lua +++ b/lua/master/init.lua @@ -4,7 +4,7 @@ ---@field default_config MasterConfig ---@field config MasterConfig ---@field state MasterState ----@field builtins MasterBuiltins +---@field builtins Builtins local M = {}; M.builtins = require("master.builtins") @@ -12,35 +12,175 @@ M.builtins = require("master.builtins") ---@class MasterConfig ---@field master_ratio number ---@field tag_count number +---@field focus_on_spawn boolean +---@field refocus_on_kill boolean +---@field screen_gap number +---@field tile_gap number local default_config = { - master_ratio = 0.5, - tag_count = 5, - applications = { - terminal = "alacritty", - browser = "zen" - }, - binds = { - spawn_terminal = { lhs = { mod = "alt", key = "Return" }, rhs = M.builtins.spawn_terminal }, - spawn_run_launcher = { lhs = { mod = "alt", key = "p" }, rhs = M.builtins.spawn_run_launcher }, - close_focused = { lhs = { mods = "alt|shift", key = "C", rhs = M.builtins.close_focused }, - } + master_ratio = 0.5, + tag_count = 5, + focus_on_spawn = true, + refocus_on_kill = true, + screen_gap = 10, + tile_gap = 10 } ----@class Tag ----@field floating number[] +---@class MasterTag +---@field master number | nil ---@field stack number[] +---@field floating number[] + +---@param config MasterConfig +---@return MasterState +local create_state = function(config) + ---@type MasterTag[] + local tags = {} + + for i = 1,config.tag_count do + tags[i] = { + master = nil, + floating = {}, + stack = {} + } + end + + return { + tag_id = 0, + tags = tags + } +end ---@class MasterState ---@field tag_id number ----@field tags Tag[] -M.state = { - tag_id = 1, - tags = {} -} +---@field tags MasterTag[] +---@field master_ratio number +---@param config MasterConfig M.setup = function(config) + --- Here we need to validate the config + --- DON'T FORGET TO VALIDATE THE CONFIG + M.state = create_state(config) + + ---@param tag_id number + local tile_tag = function(tag_id) + local tag = M.state.tags[tag_id] + + local res = mez.output.get_resolution(0) + + if #tag.stack == 0 then + mez.view.set_position(tag.master, config.screen_gap, config.screen_gap) + mez.view.set_size( + tag.master, + res.width - config.screen_gap * 2, + res.height - config.screen_gap * 2) + else + mez.view.set_position(tag.master, config.screen_gap, config.screen_gap) + mez.view.set_size( + tag.master, + res.width * M.state.master_ratio - config.screen_gap - config.tile_gap, + res.height - config.screen_gap * 2) + + local stack_x = (res.width * (1 - M.state.master_ratio)) - config.tile_gap - config.screen_gap + local stack_width = res.width * (1 - M.state.master_ratio) - config.tile_gap - config.screen_gap + + for i, view_id in ipairs(tag.stack) do + mez.view.set_position(view_id, + stack_x, + (res.height / #tag.stack + config.tile_gap) * (i - 1) + config.screen_gap) + + mez.view.set_size(view_id, + stack_width, + res.height / #tag.stack - config.screen_gap * 2 - config.tile_gap * (#tag.stack - 1)) + end + end + end + + mez.hook.add("ViewMapPre", { + callback = function(view_id) + local curr_tag = M.state.tags[M.state.tag_id] + + if curr_tag.master == nil then + curr_tag.master = view_id + else + table.insert(curr_tag.stack, #curr_tag.stack + 1, view_id) + end + + if config.focus_on_spawn then mez.view.set_focused(view_id) end + + tile_tag(M.state.tag_id) + end + }) + + mez.hook.add("ViewUnmapPost", { + callback = function(view_id) + ---@type number | nil + local tag_idx = nil + + ---@type "master" | "stacking" | "floating" | nil + local type = nil + + ---@type number | nil + local view_idx = nil + + for i, curr_tag in ipairs[M.state.tags] do + local t = M.state.tags[i] + + --- If view is the master + if view_id == curr_tag.master then + tag_idx = i + type = "master" + break + end + + --- If view is floating + for j, curr_view in ipairs(t.floating) do + if curr_view == view_id then + tag_idx = i + type = "floating" + view_idx = j + break; + end + end + + --- If view is stacking + for j, curr_view in ipairs(t.stack) do + if curr_view == view_id then + tag_idx = i + type = "stacking" + view_idx = j + break; + end + end + end + + local tag = M.state.tags[tag_idx] + + --- Now remove the view and retile if needed + if type == "floating" then + table.remove(tag.floating, view_idx) + elseif type == "master" then + tag.master = table.remove(tag.stack, 1) + tile_tag(tag_idx) + + if config.refocus_on_kill then + mez.view.set_focused(tag.master) + end + elseif type == "stacking" then + local is_last = #tag.stack == view_idx + table.remove(tag.stack, view_idx) + tile_tag(tag_idx) + + if config.refocus_on_kill then + if #tag.stack == 0 then + mez.view.set_focus(tag.master) + else + mez.view.set_focused(is_last and view_idx - 1 or view_idx) + end + end + end + end + }) end return M -