Draft mode

This commit is contained in:
Xinhao Yuan 2019-07-31 21:00:38 -04:00
parent 11c804266c
commit 752539bba5
5 changed files with 340 additions and 163 deletions

View File

@ -53,11 +53,12 @@ The editor is keyboard driven, each command is a key with optional digits (namel
1. `Up`/`Down`: restore to the history command sequence 1. `Up`/`Down`: restore to the history command sequence
2. `h`/`v`: split the current region horizontally/vertically into `#D` regions. The split will respect the ratio of digits in `D`. 2. `h`/`v`: split the current region horizontally/vertically into `#D` regions. The split will respect the ratio of digits in `D`.
3. `w`: Take the last two digits from `D` as `D = ...AB` (1 if `D` is shorter than 2 digits), and split the current region equally into A rows and B columns. If no digits are provided at all, behave the same as `Space`. 3. `w`: Take the last two digits from `D` as `D = ...AB` (1 if `D` is shorter than 2 digits), and split the current region equally into A rows and B columns. If no digits are provided at all, behave the same as `Space`.
4. `s`: shift the current editing region with other open regions. If digits are provided, shift for that many times. 4. `d`: Take the argument in the format of `A0B`, where `A` and `B` do not contain any `0`, apply `h` with argument `A` unless `A` is shorter than 2 digits. On each splitted region, apply `v` with argument `B` unless `B` is shorter than 2 digit. Does nothing if the argument is ill-formed.
5. `Space` or `-`: Without parameters, close the current region and move to the next open region. With digits, set the maximum depth of splitting (the default depth is 2). 5. `s`: shift the current editing region with other open regions. If digits are provided, shift for that many times.
6. `Enter`/`.`: close all open regions. When all regions are closed, press `Enter` will save the layout and exit the editor. 6. `Space` or `-`: Without parameters, close the current region and move to the next open region. With digits, set the maximum depth of splitting (the default depth is 2).
7. `Backspace`: undo the last command. 7. `Enter`/`.`: close all open regions. When all regions are closed, press `Enter` will save the layout and exit the editor.
8. `Escape`: exit the editor without saving the layout. 8. `Backspace`: undo the last command.
9. `Escape`: exit the editor without saving the layout.
For examples: For examples:
@ -107,6 +108,32 @@ Tada!
``` ```
`12210121d`
```
11 2222 3333 44
11 2222 3333 44
55 6666 7777 88
55 6666 7777 88
55 6666 7777 88
55 6666 7777 88
99 AAAA BBBB CC
99 AAAA BBBB CC
```
### Draft mode
__This mode is experimental. Its usage may change fast.__
Unlike the original machi layout, where a window fits in a single region, draft mode allows window to span across multiple regions.
Each tiled window is associated with a upper-left region (ULR) and a bottom-right region (BRR).
The geometry of the window is from the upper-left corner of the ULR to the bottom-right corner of the BRR.
This is suppose to work with regions produced with `d` command.
To enable draft mode in a layout, configure the layout with a command with a leading `d`, for example, `d12210121d`.
### Persistent history ### Persistent history
By default, the last 100 command sequences are stored in `.cache/awesome/history_machi`. By default, the last 100 command sequences are stored in `.cache/awesome/history_machi`.
@ -117,16 +144,12 @@ To change that, please refer to `editor.lua`. (XXX more documents)
Calling `machi.switcher.start()` will create a switcher supporting the following keys: Calling `machi.switcher.start()` will create a switcher supporting the following keys:
- Arrow keys: move focus into other regions by the direction. - Arrow keys: move focus into other regions by the direction.
- `Shift` + arrow keys: move the focused window to other regions by the direction. - `Shift` + arrow keys: move the focused window to other regions by the direction. In draft mode, move the upper-left region by direction.
- `Tab`: switch windows in the same regions. - `Control` + arrow keys: move the bottom-right region of the focused window by direction. Only work in draft mode.
- `Tab`: switch beteen windows covering the current regions.
So far, the key binding is not configurable. One has to modify the source code to change it. So far, the key binding is not configurable. One has to modify the source code to change it.
## Other functions
`machi.editor.fit_region(c, cycle = false)` will fit a floating client into the closest region.
If `cycle` is true, it then moves the window by cycling all regions.
## Advanced ## Advanced
### `name` as a function in `machi.layout.create` ### `name` as a function in `machi.layout.create`
@ -142,10 +165,6 @@ To differentiate tags with the same name, you may need a more advanced naming fu
2. True transparency is required. Otherwise switcher and editor will block the clients. 2. True transparency is required. Otherwise switcher and editor will block the clients.
## TODO
- Tabs on regions?
## License ## License
Apache 2.0 --- See LICENSE Apache 2.0 --- See LICENSE

View File

@ -62,42 +62,6 @@ local function max(a, b)
if a < b then return b else return a end if a < b then return b else return a end
end end
local function set_region(c, r)
c.floating = false
c.maximized = false
c.fullscreen = false
c.machi_region = r
api.layout.arrange(c.screen)
end
--- fit the client into the machi of the screen
-- @param c the client to fit
-- @param cycle whether to cycle the region if the window is already in machi
-- @return whether any actions have been taken on the client
local function fit_region(c, cycle)
local layout = api.layout.get(c.screen)
local regions = layout.machi_get_regions and layout.machi_get_regions(c.screen.workarea, c.screen.selected_tag)
if type(regions) ~= "table" or #regions < 1 then
return false
end
local current_region = c.machi_region or 1
if not is_tiling(c) then
-- find out which region has the most intersection, calculated by a cap b / a cup b
c.machi_region = machi.layout.find_region(c, regions)
set_tiling(c)
elseif cycle then
if current_region >= #regions then
c.machi_region = 1
else
c.machi_region = current_region + 1
end
api.layout.arrange(c.screen)
else
return false
end
return true
end
local function _area_tostring(wa) local function _area_tostring(wa)
return "{x:" .. tostring(wa.x) .. ",y:" .. tostring(wa.y) .. ",w:" .. tostring(wa.width) .. ",h:" .. tostring(wa.height) .. "}" return "{x:" .. tostring(wa.x) .. ",y:" .. tostring(wa.y) .. ",w:" .. tostring(wa.width) .. ",h:" .. tostring(wa.height) .. "}"
end end
@ -385,6 +349,64 @@ local function create(data)
open_areas[#open_areas + 1] = children[i] open_areas[#open_areas + 1] = children[i]
end end
elseif method == "d" then
local x_shares = {}
local y_shares = {}
local current = x_shares
for i = 1, #args do
local arg
if not alt then
arg = tonumber(args:sub(i, i))
else
arg = tonumber(args:sub(#args - i + 1, #args - i + 1))
end
if arg == 0 then
if current == x_shares then current = y_shares else break end
else
current[#current + 1] = arg
end
end
if #x_shares == 0 or #y_shares == 0 then
open_areas[#open_areas + 1] = a
return
end
x_shares = fair_split(a.width, x_shares)
y_shares = fair_split(a.height, y_shares)
local children = {}
for y_index = 1, #y_shares do
for x_index = 1, #x_shares do
local r = {
x = x_index == 1 and a.x or children[#children].x + children[#children].width,
y = y_index == 1 and a.y or (x_index == 1 and children[#children].y + children[#children].height or children[#children].y),
width = x_shares[x_index],
height = y_shares[y_index],
depth = a.depth + 1,
group_id = split_count,
}
if x_index == 1 then r.bl = a.bl else r.bl = false end
if x_index == #x_shares then r.br = a.br else r.br = false end
if y_index == 1 then r.bu = a.bu else r.bu = false end
if y_index == #y_shares then r.bd = a.bd else r.bd = false end
children[#children + 1] = r
end
end
for i = #children, 1, -1 do
if children[i].x ~= math.floor(children[i].x)
or children[i].y ~= math.floor(children[i].y)
or children[i].width ~= math.floor(children[i].width)
or children[i].height ~= math.floor(children[i].height)
then
print("warning, splitting yields floating area " .. _area_tostring(children[i]))
end
open_areas[#open_areas + 1] = children[i]
end
elseif method == "p" then elseif method == "p" then
-- XXX -- XXX
end end
@ -407,6 +429,8 @@ local function create(data)
else else
handle_split("w", key == "W") handle_split("w", key == "W")
end end
elseif key == "d" or key == "D" then
handle_split("d", key == "D")
elseif key == "p" or key == "P" then elseif key == "p" or key == "P" then
handle_split("p", key == "P") handle_split("p", key == "P")
elseif key == "s" or key == "S" then elseif key == "s" or key == "S" then
@ -578,7 +602,6 @@ local function create(data)
infobox.bgimage = draw_info infobox.bgimage = draw_info
end end
print("interactive layout editing starts") print("interactive layout editing starts")
init(screen.workarea) init(screen.workarea)
@ -760,8 +783,6 @@ end
return return
{ {
set_region = set_region,
fit_region = fit_region,
create = create, create = create,
restore_data = restore_data, restore_data = restore_data,
} }

View File

@ -1,17 +1,16 @@
local layout = require(... .. ".layout") local layout = require(... .. ".layout")
local editor = require(... .. ".editor") local editor = require(... .. ".editor")
local switcher = require(... .. ".switcher") local switcher = require(... .. ".switcher")
local function default_name(tag)
if tag.machi_name_cache == nil then
tag.machi_name_cache =
tostring(tag.screen.geometry.width) .. "x" .. tostring(tag.screen.geometry.height) .. "+" ..
tostring(tag.screen.geometry.x) .. "+" .. tostring(tag.screen.geometry.y) .. '+' .. tag.name
end
return tag.machi_name_cache, true
end
local default_editor = editor.create() local default_editor = editor.create()
local default_layout = layout.create( local default_layout = layout.create(default_name, default_editor)
function (tag)
if tag.machi_name_cache == nil then
tag.machi_name_cache =
tostring(tag.screen.geometry.width) .. "x" .. tostring(tag.screen.geometry.height) .. "+" ..
tostring(tag.screen.geometry.x) .. "+" .. tostring(tag.screen.geometry.y) .. '+' .. tag.name
end
return tag.machi_name_cache, true
end,
default_editor)
local gcolor = require("gears.color") local gcolor = require("gears.color")
local beautiful = require("beautiful") local beautiful = require("beautiful")

View File

@ -44,6 +44,42 @@ local function find_region(c, regions)
return choice return choice
end end
local function distance(x1, y1, x2, y2)
-- use d1
return math.abs(x1 - x2) + math.abs(y1 - y2)
end
local function find_lu(c, regions)
local lu = nil
for i, a in ipairs(regions) do
if lu == nil or distance(c.x, c.y, a.x, a.y) < distance(c.x, c.y, regions[lu].x, regions[lu].y) then
lu = i
end
end
return lu
end
local function find_rd(c, regions, lu)
assert(lu ~= nil)
local rd = nil
for i, a in ipairs(regions) do
if a.x + a.width > regions[lu].x and a.y + a.height > regions[lu].y then
if rd == nil or distance(c.x + c.width, c.y + c.height, a.x + a.width, a.y + a.height) < distance(c.x + c.width, c.y + c.height, regions[rd].x + regions[rd].width, regions[rd].y + regions[rd].height) then
rd = i
end
end
end
return rd
end
local function set_geometry(c, region_lu, region_rd, useless_gap, border_width)
-- We try to negate the gap of outer layer8
c.x = region_lu.x - useless_gap
c.y = region_lu.y - useless_gap
c.width = region_rd.x + region_rd.width - region_lu.x + (useless_gap - border_width) * 2
c.height = region_rd.y + region_rd.height - region_lu.y + (useless_gap - border_width) * 2
end
local function create(name, editor) local function create(name, editor)
local instances = {} local instances = {}
@ -67,13 +103,13 @@ local function create(name, editor)
local function get_regions(workarea, tag) local function get_regions(workarea, tag)
local instance = get_instance(tag) local instance = get_instance(tag)
if instance.cmd == nil then return {} end if instance.cmd == nil then return {}, false end
local key = tostring(workarea.width) .. "x" .. tostring(workarea.height) .. "+" .. tostring(workarea.x) .. "+" .. tostring(workarea.y) local key = tostring(workarea.width) .. "x" .. tostring(workarea.height) .. "+" .. tostring(workarea.x) .. "+" .. tostring(workarea.y)
if instance.regions_cache[key] == nil then if instance.regions_cache[key] == nil then
instance.regions_cache[key] = editor.run_cmd(workarea, instance.cmd) instance.regions_cache[key] = editor.run_cmd(workarea, instance.cmd)
end end
return instance.regions_cache[key] return instance.regions_cache[key], instance.cmd:sub(1,1) == "d"
end end
local function set_cmd(cmd, tag) local function set_cmd(cmd, tag)
@ -88,69 +124,122 @@ local function create(name, editor)
local useless_gap = p.useless_gap local useless_gap = p.useless_gap
local wa = get_screen(p.screen).workarea -- get the real workarea without the gap (instead of p.workarea) local wa = get_screen(p.screen).workarea -- get the real workarea without the gap (instead of p.workarea)
local cls = p.clients local cls = p.clients
local regions = get_regions(wa, get_screen(p.screen).selected_tag) local regions, draft_mode = get_regions(wa, get_screen(p.screen).selected_tag)
if #regions == 0 then return end if #regions == 0 then return end
for i, c in ipairs(cls) do if draft_mode then
if c.floating then for i, c in ipairs(cls) do
print("Ignore client " .. tostring(c)) if c.floating then
else else
if c.machi_region == nil then local skip = false
c.machi_region = find_region(c, regions) if c.machi_lu ~= nil and c.machi_rd ~= nil and
elseif c.machi_region > #regions then c.machi_lu <= #regions and c.machi_rd <= #regions
c.machi_region = #regions then
elseif c.machi_region <= 1 then if regions[c.machi_lu].x == c.x and
c.machi_region = 1 regions[c.machi_lu].y == c.y and
regions[c.machi_rd].x + regions[c.machi_rd].width - c.border_width * 2 == c.x + c.width and
regions[c.machi_rd].y + regions[c.machi_rd].height - c.border_width * 2 == c.y + c.height
then
skip = true
end
end
local lu = nil
local rd = nil
if not skip then
print("Compute regions for " .. c.name)
lu = find_lu(c, regions)
if lu ~= nil then
rd = find_rd(c, regions, lu)
end
end
if lu ~= nil and rd ~= nil then
c.machi_lu, c.machi_rd = lu, rd
p.geometries[c] = {}
set_geometry(p.geometries[c], regions[lu], regions[rd], useless_gap, 0)
end
end
end
else
for i, c in ipairs(cls) do
if c.floating then
print("Ignore client " .. tostring(c))
else
if c.machi_region ~= nil and
regions[c.machi_region].x == c.x and
regions[c.machi_region].y == c.y and
regions[c.machi_region].width - c.border_width * 2 == c.width and
regions[c.machi_region].height - c.border_width * 2 == c.height
then
else
print("Compute regions for " .. c.name)
local region = find_region(c, regions)
c.machi_region = region
p.geometries[c] = {}
set_geometry(p.geometries[c], regions[region], regions[region], useless_gap, 0)
end
end end
local region = c.machi_region
-- Editor already handled useless_gap in the stored regions.
-- We try to negate the gap of outer layer.
p.geometries[c] = {
x = regions[region].x - useless_gap,
y = regions[region].y - useless_gap,
width = regions[region].width + useless_gap * 2,
height = regions[region].height + useless_gap * 2,
}
print("Put client " .. tostring(c) .. " to region " .. region)
end end
end end
end end
-- move the closest region regardingly to the center distance local function resize_handler (c, context, h)
local function resize_handler(c, context, h)
if context ~= "mouse.move" then return end
local workarea = c.screen.workarea local workarea = c.screen.workarea
local regions = get_regions(workarea, c.screen.selected_tag) local regions, draft_mode = get_regions(workarea, c.screen.selected_tag)
if #regions == 0 then return end if #regions == 0 then return end
local center_x = h.x + h.width / 2 if draft_mode then
local center_y = h.y + h.height / 2 local lu = find_lu(h, regions)
local rd = nil
if lu ~= nil then
if context == "mouse.move" then
local hh = {}
hh.x = regions[lu].x
hh.y = regions[lu].y
hh.width = h.width
hh.height = h.height
rd = find_rd(hh, regions, lu)
else
rd = find_rd(h, regions, lu)
end
local choice = 1 if rd ~= nil then
local choice_value = nil c.machi_lu = lu
c.machi_rd = rd
for i, r in ipairs(regions) do set_geometry(c, regions[lu], regions[rd], 0, c.border_width)
local r_x = r.x + r.width / 2 end
local r_y = r.y + r.height / 2
local dis = (r_x - center_x) * (r_x - center_x) + (r_y - center_y) * (r_y - center_y)
if choice_value == nil or choice_value > dis then
choice = i
choice_value = dis
end end
end else
if context ~= "mouse.move" then return end
if c.machi_region ~= choice then local workarea = c.screen.workarea
c.machi_region = choice local regions = get_regions(workarea, c.screen.selected_tag)
c.x = regions[choice].x
c.y = regions[choice].y if #regions == 0 then return end
c.width = max(1, regions[choice].width - 2 * c.border_width)
c.height = max(1, regions[choice].height - 2 * c.border_width) local center_x = h.x + h.width / 2
local center_y = h.y + h.height / 2
local choice = 1
local choice_value = nil
for i, r in ipairs(regions) do
local r_x = r.x + r.width / 2
local r_y = r.y + r.height / 2
local dis = (r_x - center_x) * (r_x - center_x) + (r_y - center_y) * (r_y - center_y)
if choice_value == nil or choice_value > dis then
choice = i
choice_value = dis
end
end
if c.machi_region ~= choice then
c.machi_region = choice
set_geometry(c, regions[choice], regions[choice], 0, c.border_width)
end
end end
end end
@ -166,5 +255,5 @@ end
return { return {
create = create, create = create,
find_region = find_region, set_geometry = set_geometry,
} }

View File

@ -1,3 +1,7 @@
local machi = {
layout = require((...):match("(.-)[^%.]+$") .. "layout"),
}
local api = { local api = {
client = client, client = client,
beautiful = require("beautiful"), beautiful = require("beautiful"),
@ -11,19 +15,6 @@ local api = {
dpi = require("beautiful.xresources").apply_dpi, dpi = require("beautiful.xresources").apply_dpi,
} }
-- -- Seems not needed?
-- local focus_timer = 0
-- api.client.connect_signal(
-- "focus",
-- function (c)
-- if c.focus_timer == nil or c.focus_timer < focus_timer then
-- c.focus_timer = focus_timer
-- end
-- focus_timer = c.focus_timer + 1
-- c.focus_timer = focus_timer
-- end
-- )
local function min(a, b) local function min(a, b)
if a < b then return a else return b end if a < b then return a else return b end
end end
@ -59,7 +50,7 @@ local function start(c)
local layout = api.layout.get(screen) local layout = api.layout.get(screen)
if c.floating or layout.machi_get_regions == nil then return end if c.floating or layout.machi_get_regions == nil then return end
local regions = layout.machi_get_regions(c.screen.workarea, c.screen.selected_tag) local regions, draft_mode = layout.machi_get_regions(c.screen.workarea, c.screen.selected_tag)
local infobox = api.wibox({ local infobox = api.wibox({
screen = screen, screen = screen,
@ -73,6 +64,7 @@ local function start(c)
}) })
infobox.visible = true infobox.visible = true
local tablist_region = nil
local tablist = nil local tablist = nil
local tablist_index = nil local tablist_index = nil
@ -83,12 +75,13 @@ local function start(c)
if tablist == nil then if tablist == nil then
tablist = {} tablist = {}
for _, tc in ipairs(screen.tiled_clients) do for _, tc in ipairs(screen.tiled_clients) do
if tc.machi_region == c.machi_region if not (tc.floating or tc.maximized or tc.maximized_horizontal or tc.maximized_vertical)
and not tc.maximized
and not tc.maximized_horizontal
and not tc.maximized_vertical
then then
tablist[#tablist + 1] = tc if tc.x <= traverse_x and traverse_x < tc.x + tc.width and
tc.y <= traverse_y and traverse_y < tc.y + tc.height
then
tablist[#tablist + 1] = tc
end
end end
end end
@ -108,7 +101,8 @@ local function start(c)
cr:rectangle(a.x - start_x, a.y - start_y, a.width, a.height) cr:rectangle(a.x - start_x, a.y - start_y, a.width, a.height)
cr:clip() cr:clip()
if i == c.machi_region then if a.x <= traverse_x and traverse_x < a.x + a.width and
a.y <= traverse_y and traverse_y < a.y + a.height then
local pl = api.lgi.Pango.Layout.create(cr) local pl = api.lgi.Pango.Layout.create(cr)
pl:set_font_description(tablist_font_desc) pl:set_font_description(tablist_font_desc)
@ -132,7 +126,9 @@ local function start(c)
local x_offset = a.x + a.width / 2 - start_x local x_offset = a.x + a.width / 2 - start_x
local y_offset = a.y + a.height / 2 - list_height / 2 + vpadding - start_y local y_offset = a.y + a.height / 2 - list_height / 2 + vpadding - start_y
cr:rectangle(a.x - start_x, y_offset - vpadding - start_y, a.width, list_height) -- cr:rectangle(a.x - start_x, y_offset - vpadding - start_y, a.width, list_height)
-- cover the entire region
cr:rectangle(a.x - start_x, a.y - start_y, a.width, a.height)
cr:set_source(fill_color) cr:set_source(fill_color)
cr:fill() cr:fill()
@ -192,10 +188,33 @@ local function start(c)
infobox.bgimage = draw_info infobox.bgimage = draw_info
end end
elseif key == "Up" or key == "Down" or key == "Left" or key == "Right" then elseif key == "Up" or key == "Down" or key == "Left" or key == "Right" then
local shift = false
local ctrl = false
for i, m in ipairs(mod) do
if m == "Shift" then shift = true
elseif m == "Control" then ctrl = true
end
end
if shift then
traverse_x = c.x + traverse_radius
traverse_y = c.y + traverse_radius
elseif ctrl then
traverse_x = c.x + c.width - c.border_width * 2 - traverse_radius
traverse_y = c.y + c.height - c.border_width * 2 - traverse_radius
end
local choice = nil local choice = nil
local choice_value local choice_value
local current_region = nil
for i, a in ipairs(regions) do for i, a in ipairs(regions) do
if a.x <= traverse_x and traverse_x < a.x + a.width and
a.y <= traverse_y and traverse_y < a.y + a.height
then
current_region = i
end
local v local v
if key == "Up" then if key == "Up" then
if a.x < traverse_x + threshold if a.x < traverse_x + threshold
@ -233,43 +252,73 @@ local function start(c)
end end
end end
if choice ~= nil and choice_value > threshold then if choice == nil then
local shift = false choice = current_region
for i, m in ipairs(mod) do if key == "Up" then
if m == "Shift" then shift = true end traverse_y = screen.workarea.y
elseif key == "Down" then
traverse_y = screen.workarea.y + screen.workarea.height
elseif key == "Left" then
traverse_x = screen.workarea.x
else
traverse_x = screen.workarea.x + screen.workarea.width
end end
end
local move_traverse = false if choice ~= nil then
if shift then if shift then
-- move the window if draft_mode then
c.machi_region = choice -- move the left-up region
local lu = choice
local rd = c.machi_rd
if regions[rd].x + regions[rd].width <= regions[lu].x or
regions[rd].y + regions[rd].height <= regions[lu].y
then
rd = lu
end
machi.layout.set_geometry(c, regions[lu], regions[rd], 0, c.border_width)
c.machi_lu = lu
c.machi_rd = rd
else
-- move the window
machi.layout.set_geometry(c, regions[choice], regions[choice], 0, c.border_width)
c.machi_region = choice
end
c:emit_signal("request::activate", "mouse.move", {raise=false}) c:emit_signal("request::activate", "mouse.move", {raise=false})
c:raise() c:raise()
api.layout.arrange(screen) api.layout.arrange(screen)
move_traverse = true
tablist = nil
elseif ctrl and draft_mode then
-- move the right-down region
local lu = c.machi_lu
local rd = choice
if regions[rd].x + regions[rd].width <= regions[lu].x or
regions[rd].y + regions[rd].height <= regions[lu].y
then
lu = rd
end
machi.layout.set_geometry(c, regions[lu], regions[rd], 0, c.border_width)
c.machi_lu = lu
c.machi_rd = rd
c:emit_signal("request::activate", "mouse.move", {raise=false})
c:raise()
api.layout.arrange(screen)
tablist = nil
else else
-- move the focus -- move the focus
for _, tc in ipairs(screen.tiled_clients) do
if tc.machi_region == choice
and not tc.maximized
and not tc.maximized_horizontal
and not tc.maximized_vertical
then
c = tc
api.client.focus = c
break
end
end
move_traverse = true
end
if move_traverse then
traverse_x = max(regions[choice].x + traverse_radius, min(regions[choice].x + regions[choice].width - traverse_radius, traverse_x))
traverse_y = max(regions[choice].y + traverse_radius, min(regions[choice].y + regions[choice].height - traverse_radius, traverse_y))
tablist = nil tablist = nil
ensure_tablist()
if #tablist > 0 and tablist[1] ~= c then
c = tablist[1]
api.client.focus = c
end
end end
traverse_x = max(regions[choice].x + traverse_radius, min(regions[choice].x + regions[choice].width - traverse_radius, traverse_x))
traverse_y = max(regions[choice].y + traverse_radius, min(regions[choice].y + regions[choice].height - traverse_radius, traverse_y))
infobox.bgimage = draw_info infobox.bgimage = draw_info
end end
elseif key == "Escape" or key == "Return" then elseif key == "Escape" or key == "Return" then