set layout directly by command. restore the last layout

This commit is contained in:
Xinhao Yuan 2019-07-05 21:57:27 -04:00
parent 358c2cee18
commit 8891f371a8
3 changed files with 294 additions and 265 deletions

View File

@ -12,19 +12,19 @@ TL;DR --- I want the control of my layout.
## Use the layout
Use `layout_machi.layout.create_layout([LAYOUT_NAME}, [DEFAULT_REGIONS])` to instantiate the layout.
For example:
```
layout_machi.layout.create_layout("default", {})
```
Creates a layout with no regions
Use `layotu = layout_machi.layout.create()` to instantiate the layout.
## Use the editor
Call `layout_machi.editor.start_editor(data)` to enter the editor for the current layout (given it is a machi instance).
`data` is am object for storing the history of the editing, initially `{}`.
Call `editor = layout_machi.editor.create(data)` to create an editor that can either
- Interactively edit layout by calling `editor.start_interactive()`
- Set the layout with batched commands by calling `editor.set_by_cmd(cmd)`, where cmd is a string
`data` is an object for storing the history of the editing, initially `{}`.
### The layout editing command
The editor starts with the open area of the entire workarea, taking command to split the current area into multiple sub-areas, then editing each of them.
The editor is keyboard driven, accepting a number of command keys.
Before each command, you can optionally provide at most 2 digits for parameters (A, B) of the command.
@ -39,10 +39,7 @@ Undefined parameters are (mostly) treated as 1.
7. `Backspace`: undo the last command.
8. `Escape`: exit the editor without saving the layout.
### Demos:
I used `Super + /` for the editor and `Super + Tab` for fitting the windows.
For examples:
h-v
@ -54,8 +51,6 @@ h-v
11 33
```
![](https://i.imgur.com/QbvMRTW.gif)
hvv (or 22w)
@ -67,8 +62,6 @@ hvv (or 22w)
22 44
```
![](https://i.imgur.com/xJebxcF.gif)
3-13h2v--2h-12v
@ -97,24 +90,17 @@ Tada!
```
history
![](https://i.imgur.com/gzFr48V.gif)
### Persistent history
You need to specify the path of the history file in the editor data, then restore the persistent history by `layout_machi.editor.restore_data`. For example,
If you want all command persisted, you need to specify the path of the history file in the editor data.
The persisted history can be restored by `layout_machi.editor.restore_data(...)`. For example,
```
machi_layout_data = layout_machi.editor.restore_data({ history_file = ".machi-layout", history_save_max = 10 })
machi_editor_data = layout_machi.editor.restore_data({ history_file = ".machi-layout", history_save_max = 10 })
```
Then start the editor with the restored data.
The last `history_save_max` commands are persisted.
## Other goodies
- Moving a window using the mouse will move it across regions
## Other functions

View File

@ -107,40 +107,9 @@ function shrink_area_with_gap(a, gap)
height = a.height - (a.bu and 0 or gap / 2) - (a.bd and 0 or gap / 2) }
end
function start_editor(data)
function create(data)
local gap = data.gap or 0
if data.cmds == nil then
data.cmds = {}
end
local cmd_index = #data.cmds + 1
data.cmds[cmd_index] = ""
local screen = api.screen.focused()
local init_area = {
x = screen.workarea.x,
y = screen.workarea.y,
width = screen.workarea.width,
height = screen.workarea.height,
border = 15,
depth = 0,
group_id = 0,
-- we do not want to rely on BitOp
bl = true, br = true, bu = true, bd = true,
}
local kg
local infobox = api.wibox({
x = screen.workarea.x,
y = screen.workarea.y,
width = screen.workarea.width,
height = screen.workarea.height,
bg = "#ffffff00",
opacity = 1,
ontop = true
})
infobox.visible = true
local closed_areas
local open_areas
local history
@ -152,9 +121,21 @@ function start_editor(data)
local to_exit
local to_apply
local function init()
local function init(init_area)
closed_areas = {}
open_areas = {init_area}
open_areas = {
{
x = init_area.x,
y = init_area.y,
width = init_area.width,
height = init_area.height,
border = 15,
depth = 0,
group_id = 0,
-- we do not want to rely on BitOp
bl = true, br = true, bu = true, bd = true,
}
}
history = {}
num_1 = nil
num_2 = nil
@ -165,68 +146,6 @@ function start_editor(data)
to_apply = false
end
local function draw_info(context, cr, width, height)
cr:set_source_rgba(0, 0, 0, 0)
cr:rectangle(0, 0, width, height)
cr:fill()
local msg, ext
for i, a in ipairs(closed_areas) do
local sa = shrink_area_with_gap(a, gap)
cr:rectangle(sa.x, sa.y, sa.width, sa.height)
cr:clip()
cr:set_source(api.gears.color(closed_color))
cr:rectangle(sa.x, sa.y, sa.width, sa.height)
cr:fill()
cr:set_source(api.gears.color(border_color))
cr:rectangle(sa.x, sa.y, sa.width, sa.height)
cr:set_line_width(10.0)
cr:stroke()
cr:reset_clip()
end
for i, a in ipairs(open_areas) do
local sa = shrink_area_with_gap(a, gap)
cr:rectangle(sa.x, sa.y, sa.width, sa.height)
cr:clip()
if i == #open_areas then
cr:set_source(api.gears.color(active_color))
else
cr:set_source(api.gears.color(open_color))
end
cr:rectangle(sa.x, sa.y, sa.width, sa.height)
cr:fill()
cr:set_source(api.gears.color(border_color))
cr:rectangle(sa.x, sa.y, sa.width, sa.height)
cr:set_line_width(10.0)
if i ~= #open_areas then
cr:set_dash({5, 5}, 0)
cr:stroke()
cr:set_dash({}, 0)
else
cr:stroke()
end
cr:reset_clip()
end
cr:select_font_face(label_font_family, "normal", "normal")
cr:set_font_size(info_size)
cr:set_font_face(cr:get_font_face())
msg = current_info
ext = cr:text_extents(msg)
cr:move_to(width / 2 - ext.width / 2 - ext.x_bearing, height / 2 - ext.height / 2 - ext.y_bearing)
cr:text_path(msg)
cr:set_source_rgba(1, 1, 1, 1)
cr:fill()
cr:move_to(width / 2 - ext.width / 2 - ext.x_bearing, height / 2 - ext.height / 2 - ext.y_bearing)
cr:text_path(msg)
cr:set_source_rgba(0, 0, 0, 1)
cr:set_line_width(2.0)
cr:stroke()
end
local function push_history()
history[#history + 1] = {#closed_areas, #open_areas, {}, current_info, current_cmd, max_depth, num_1, num_2}
end
@ -269,18 +188,6 @@ function start_editor(data)
return a
end
local function refresh()
print("closed areas:")
for i, a in ipairs(closed_areas) do
print(" " .. _area_tostring(a))
end
print("open areas:")
for i, a in ipairs(open_areas) do
print(" " .. _area_tostring(a))
end
infobox.bgimage = draw_info
end
local split_count = 0
local function handle_split(method, alt)
@ -362,13 +269,8 @@ function start_editor(data)
num_2 = nil
end
local function cleanup()
infobox.visible = false
end
local function push_area()
closed_areas[#closed_areas + 1] = pop_open_area()
infobox.bgimage = draw_info
end
local function handle_command(key)
@ -434,9 +336,109 @@ function start_editor(data)
return key
end
local function start_interactive()
if data.cmds == nil then
data.cmds = {}
end
local cmd_index = #data.cmds + 1
data.cmds[cmd_index] = ""
local screen = api.screen.focused()
local kg
local infobox = api.wibox({
x = screen.workarea.x,
y = screen.workarea.y,
width = screen.workarea.width,
height = screen.workarea.height,
bg = "#ffffff00",
opacity = 1,
ontop = true
})
infobox.visible = true
local function cleanup()
infobox.visible = false
end
local function draw_info(context, cr, width, height)
cr:set_source_rgba(0, 0, 0, 0)
cr:rectangle(0, 0, width, height)
cr:fill()
local msg, ext
for i, a in ipairs(closed_areas) do
local sa = shrink_area_with_gap(a, gap)
cr:rectangle(sa.x, sa.y, sa.width, sa.height)
cr:clip()
cr:set_source(api.gears.color(closed_color))
cr:rectangle(sa.x, sa.y, sa.width, sa.height)
cr:fill()
cr:set_source(api.gears.color(border_color))
cr:rectangle(sa.x, sa.y, sa.width, sa.height)
cr:set_line_width(10.0)
cr:stroke()
cr:reset_clip()
end
for i, a in ipairs(open_areas) do
local sa = shrink_area_with_gap(a, gap)
cr:rectangle(sa.x, sa.y, sa.width, sa.height)
cr:clip()
if i == #open_areas then
cr:set_source(api.gears.color(active_color))
else
cr:set_source(api.gears.color(open_color))
end
cr:rectangle(sa.x, sa.y, sa.width, sa.height)
cr:fill()
cr:set_source(api.gears.color(border_color))
cr:rectangle(sa.x, sa.y, sa.width, sa.height)
cr:set_line_width(10.0)
if i ~= #open_areas then
cr:set_dash({5, 5}, 0)
cr:stroke()
cr:set_dash({}, 0)
else
cr:stroke()
end
cr:reset_clip()
end
cr:select_font_face(label_font_family, "normal", "normal")
cr:set_font_size(info_size)
cr:set_font_face(cr:get_font_face())
msg = current_info
ext = cr:text_extents(msg)
cr:move_to(width / 2 - ext.width / 2 - ext.x_bearing, height / 2 - ext.height / 2 - ext.y_bearing)
cr:text_path(msg)
cr:set_source_rgba(1, 1, 1, 1)
cr:fill()
cr:move_to(width / 2 - ext.width / 2 - ext.x_bearing, height / 2 - ext.height / 2 - ext.y_bearing)
cr:text_path(msg)
cr:set_source_rgba(0, 0, 0, 1)
cr:set_line_width(2.0)
cr:stroke()
end
local function refresh()
print("closed areas:")
for i, a in ipairs(closed_areas) do
print(" " .. _area_tostring(a))
end
print("open areas:")
for i, a in ipairs(open_areas) do
print(" " .. _area_tostring(a))
end
infobox.bgimage = draw_info
end
print("interactive layout editing starts")
init()
init(screen.workarea)
refresh()
kg = keygrabber.run(function (mod, key, event)
@ -461,7 +463,7 @@ function start_editor(data)
end
print("restore history #" .. tostring(cmd_index) .. ":" .. data.cmds[cmd_index])
init()
init(screen.workarea)
for i = 1, #data.cmds[cmd_index] do
cmd = data.cmds[cmd_index]:sub(i, i)
@ -528,7 +530,7 @@ function start_editor(data)
if to_exit then
print("interactive layout editing ends")
if to_apply then
layout = api.layout.get(screen)
local layout = api.layout.get(screen)
if layout.set_regions then
local areas_with_gap = {}
for _, a in ipairs(closed_areas) do
@ -546,6 +548,7 @@ function start_editor(data)
end
end
)
layout.cmd = current_cmd
layout.set_regions(areas_with_gap)
api.layout.arrange(screen)
end
@ -564,6 +567,49 @@ function start_editor(data)
end)
end
local function set_by_cmd(layout, screen, cmd)
init(screen.workarea)
push_history()
for i = 1, #cmd do
local key = handle_command(cmd:sub(i, i))
end
local areas_with_gap = {}
for _, a in ipairs(closed_areas) do
areas_with_gap[#areas_with_gap + 1] = shrink_area_with_gap(a, gap)
end
table.sort(
areas_with_gap,
function (a1, a2)
local s1 = a1.width * a1.height
local s2 = a2.width * a2.height
if math.abs(s1 - s2) < 0.01 then
return (a1.x + a1.y) < (a2.x + a2.y)
else
return s1 > s2
end
end
)
layout.cmd = cmd
layout.set_regions(areas_with_gap)
api.layout.arrange(screen)
end
local function try_restore_last(layout, screen)
local index = #data.cmds
if index == 0 then return end
set_by_cmd(layout, screen, data.cmds[#data.cmds])
end
return {
start_interactive = start_interactive,
set_by_cmd = set_by_cmd,
try_restore_last = try_restore_last,
}
end
function restore_data(data)
if data.history_file then
local file, err = io.open(data.history_file, "r")
@ -585,6 +631,6 @@ return
{
set_region = set_region,
cycle_region = cycle_region,
start_editor = start_editor,
create = create,
restore_data = restore_data,
}

View File

@ -32,7 +32,7 @@ function do_arrange(p, priv)
end
end
function create_layout(name, regions)
function create()
local priv = { regions = {} }
local function set_regions(regions)
@ -72,10 +72,7 @@ function create_layout(name, regions)
end
end
set_regions(regions)
return {
name = "machi[" .. name .. "]",
arrange = function (p) do_arrange(p, priv) end,
get_region_count = function () return #priv.regions end,
set_regions = set_regions,
@ -85,5 +82,5 @@ function create_layout(name, regions)
end
return {
create_layout = create_layout,
create = create,
}