aboutsummaryrefslogtreecommitdiff
path: root/src/syslua/lx/tk.lua
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2017-05-09 19:29:06 +0200
committerAlex Auvolat <alex@adnab.me>2017-05-09 19:29:06 +0200
commit83cc53def0abb6f44ebd95a7175e717e3c66cd48 (patch)
tree87e7beecd0f3f059a4a61ba0c819d2883846cee2 /src/syslua/lx/tk.lua
parentd1aed44edf1bd6ab9cc11d7c8796051a5ae8e79d (diff)
downloadkogata-83cc53def0abb6f44ebd95a7175e717e3c66cd48.tar.gz
kogata-83cc53def0abb6f44ebd95a7175e717e3c66cd48.zip
Grids & lua debugging
Diffstat (limited to 'src/syslua/lx/tk.lua')
-rw-r--r--src/syslua/lx/tk.lua415
1 files changed, 304 insertions, 111 deletions
diff --git a/src/syslua/lx/tk.lua b/src/syslua/lx/tk.lua
index 5653313..473ae3f 100644
--- a/src/syslua/lx/tk.lua
+++ b/src/syslua/lx/tk.lua
@@ -104,12 +104,15 @@ function region_diff(reg1, reg2)
end
function region_inter(reg1, reg2)
- return region_operation(reg1, reg2,
- function(x0, x1, y0, y1)
- return (x0 >= reg1.x and x0 < reg1.x + reg1.w and y0 >= reg1.y and y0 < reg1.y + reg1.h)
- and (x0 >= reg2.x and x0 < reg2.x + reg2.w and y0 >= reg2.y and y0 < reg2.y + reg2.h)
- end
- )
+ local x0 = math.max(reg1.x, reg2.x)
+ local x1 = math.min(reg1.x + reg1.w, reg2.x + reg2.w)
+ local y0 = math.max(reg1.y, reg2.y)
+ local y1 = math.min(reg1.y + reg1.h, reg2.y + reg2.h)
+ if x1 > x0 and y1 > y0 then
+ return {x = x0, y = y0, w = x1 - x0, h = y1 - y0}
+ else
+ return nil
+ end
end
function region_union(reg1, reg2)
@@ -139,37 +142,64 @@ function tk.widget(width, height, opts)
end
end
- function w:resize(width, height)
- if width then w.width = width end
- if width then w.height = height end
+ -- Funcions that must be implemented by a parent widget :
+ -- - resize_child : try to resize a child widget, checks if can be resized,
+ -- does all the required redrawing
+ -- - redraw : redraws a portion of the widget
+
+ -- can resize : checks if dimensiosn are valid
+ -- widget can replace this if necessary
+ function w:can_resize(width, height)
+ return (not width or width > 0) and (not height or height > 0)
end
- function w:get_draw_buffer(x0, y0, w, h)
- -- Replaced by parent by a function that returns the buffer for
- return nil
+ -- do resize : updates position and size values but does not redraw anything
+ -- widget must replace this when necessary !
+ function w:do_resize(width, height)
+ if width then self.width = width end
+ if height then self.height = height end
+ end
+
+ -- resize : calls all the necessary procedures for resizing and redrawing
+ -- widget must not replace this !
+ function w:resize(width, height)
+ if self.parent then
+ self.parent:resize_child(self, width, height)
+ end
end
- function w:finalize_draw_buffer()
- if self.parent ~= nil then
- self.parent:finalize_draw_buffer()
+
+ -- redraw : calls parent to redraw a portion of screen
+ -- can be replaced by widget to avoid redrawing where hidden things are
+ function w:redraw(x0, y0, w, h, who)
+ if self.parent then
+ self.parent:redraw(x0 + self.x, y0 + self.y, w, h, self)
end
end
- function w:redraw(x0, y0, buf)
- -- Replaced by widget code by a function that does the actual drawing
+ -- draw : does the actual drawing on a buffer
+ -- must be replaced by widget by code that does the drawing
+ function w:draw(x0, y0, buf)
end
- function w:redraw_sub(x0, y0, buf, subwidget)
+ -- helper function to redraw subwidget
+ -- must not be replaced by child
+ function w:draw_sub(x0, y0, buf, subwidget)
local region = {x = x0, y = y0, w = buf:width(), h = buf:height()}
local subregion = {x = subwidget.x, y = subwidget.y, w = subwidget.width, h = subwidget.height}
local inter = region_inter(region, subregion)
- for _, z in pairs(inter) do
- subwidget:redraw(z.x - subwidget.x, z.y - subwidget.y, buf:sub(z.x - x0, z.y - y0, z.w, z.h))
+ if inter then
+ subwidget:draw(inter.x - subwidget.x, inter.y - subwidget.y, buf:sub(inter.x - x0, inter.y - y0, inter.w, inter.h))
end
end
+ function w:get_child(x, y)
+ -- Replaced by widget by code that finds a child widget at given position
+ return nil
+ end
+
function w:on_mouse_down(x, y, lb, rb, mb)
- if lb then
-- Handler for mouse down event
+ if lb then
self.left_click_valid = true
end
end
@@ -222,7 +252,7 @@ function tk.init(gui, root_widget)
return gui.surface:rgb(r, g, b)
end
- root_widget:resize(gui.surface:width(), gui.surface:height())
+ root_widget:do_resize(gui.surface:width(), gui.surface:height())
gui.on_key_down = function(key) root_widget:on_key_down(key) end
gui.on_key_up = function(key) root_widget:on_key_up(key) end
@@ -231,24 +261,34 @@ function tk.init(gui, root_widget)
gui.on_mouse_down = function(lb, rb, mb) root_widget:on_mouse_down(gui.mouse_x, gui.mouse_y, lb, rb, mb) end
gui.on_mouse_up = function(lb, rb, mb) root_widget:on_mouse_up(gui.mouse_x, gui.mouse_y, lb, rb, mb) end
- function root_widget:get_draw_buffer(x0, y0, w, h)
- if gui.cursor_visible and #region_inter({x=x0, y=y0, w=w, h=h},
- {x=gui.mouse_x, y=gui.mouse_y, w=gui.cursor:width(), h=gui.cursor:height()})>0
- then
- tk.must_reshow_cursor_on_finalize = true
- gui.hide_cursor()
- end
- return gui.surface:sub(x0, y0, w, h)
- end
- function root_widget:finalize_draw_buffer()
- if tk.must_reshow_cursor_on_finalize then
- tk.must_reshow_cursor_on_finalize = false
- gui.show_cursor()
+ local root_parent = {}
+ function root_parent:resize_child(c)
+ -- NO ! Do nothing.
+ end
+ function root_parent:redraw(x0, y0, w, h)
+ local ss = region_inter({x = x0, y = y0, w = w, h = h},
+ {x = 0, y = 0, w = gui.surface:width(), h = gui.surface:height()})
+
+ if ss then
+ local must_reshow_cursor = false
+ if gui.cursor_visible and region_inter(ss, {x=gui.mouse_x, y=gui.mouse_y,
+ w=gui.cursor:width(), h=gui.cursor:height()})
+ then
+ must_reshow_cursor = true
+ gui.hide_cursor()
+ end
+
+ local buf = gui.surface:sub(ss.x, ss.y, ss.w, ss.h)
+ root_widget:draw(ss.x, ss.y, buf)
+
+ if must_reshow_cursor then
+ gui.show_cursor()
+ end
end
end
- root_widget:redraw(0, 0, root_widget:get_draw_buffer(0, 0, root_widget.width, root_widget.height))
- root_widget:finalize_draw_buffer()
+ root_widget.parent = root_parent
+ root_parent:redraw(0, 0, root_widget.width, root_widget.height)
end
-- #### #### STANDARD WIDGETS #### ####
@@ -260,18 +300,11 @@ function tk.image(a, b)
local image = tk.widget(img:width(), img:height(), opts)
image.img = img
- function image:resize(width, height)
- if width then
- self.width = width
- if self.width < self.img:width() then self.width = self.img:width() end
- end
- if height then
- self.height = height
- if self.height < self.img:height() then self.height = self.img:height() end
- end
+ function image:can_resize(width, height)
+ return width >= self.img:width() and height >= self.img:height()
end
- function image:redraw(x0, y0, buf)
+ function image:draw(x0, y0, buf)
local step = 20
local halfstep = 10
for x = x0 - (x0 % step), x0 + buf:width(), step do
@@ -307,13 +340,13 @@ function tk.text(a, b)
local txt = tk.widget(100, #string.split(text, "\n")*(opts.text_size + opts.line_spacing) - opts.line_spacing + 2*opts.padding, opts)
txt.text = text
- function txt:redraw(x0, y0, buf)
+ function txt:draw(x0, y0, buf)
local lines = string.split(self.text, "\n")
buf:fillrect(0, 0, buf:width(), buf:height(), self.background)
- local acceptable_surface = region_inter({x = x0, y = y0, w = buf:width(), h = buf:height()},
- {x = self.padding, y = self.padding, w = self.width - 2*self.padding, h = self.height - 2*self.padding})
- for _, s in pairs(acceptable_surface) do
+ local s = region_inter({x = x0, y = y0, w = buf:width(), h = buf:height()},
+ {x = self.padding, y = self.padding, w = self.width - 2*self.padding, h = self.height - 2*self.padding})
+ if s then
local buf2 = buf:sub(s.x - x0, s.y - y0, s.w, s.h)
for i = 1, #lines do
-- TODO word wrap
@@ -335,13 +368,16 @@ function tk.box(a, b)
opts.vresize = opts.vresize or false
opts.hscroll = opts.hscroll or false
opts.vscroll = opts.vscroll or false
+ opts.min_width = opts.min_width or 8
+ opts.min_height = opts.min_height or 8
opts.center_content = opts.center_content or false
opts.constrain_size = opts.constrain_size or nil
opts.background_color = opts.background_color or tk.rgb(190, 190, 190)
- opts.control_size = 12
+ opts.control_size = opts.control_size or 12
local box = tk.widget(content.width, content.height, opts)
box.content = content
+ box.content.parent = box
if box.center_content then
if box.width > box.content.width then
box.content.x = (box.width - box.content.width) // 2
@@ -358,30 +394,51 @@ function tk.box(a, b)
box.content.y = 0
end
- function box:resize(width, height)
- local ow, oh = self.width, self.height
+ function box:can_resize(width, height)
if self.constrain_size then
- self.content:resize(width, height)
- self.width = content.width
- self.height = content.height
+ return self.content:can_resize(width, height)
+ else
+ if width then
+ if self.min_width and width < self.min_width then return false end
+ end
+ if height then
+ if self.min_height and height < self.min_height then return false end
+ end
+ return true
+ end
+ end
+
+ function box:do_resize(width, height)
+ if self.constrain_size then
+ self.content:do_resize(width, height)
+ self.width = self.content.width
+ self.height = self.content.height
else
if width then self.width = width end
if height then self.height = height end
- if self.center_content then
- if self.width > self.content.width then
- self.content.x = (self.width - self.content.width) // 2
- else
- self.content.x = math.min(0, math.max(-(self.content.width - self.width), self.content.x))
- end
- if self.height > self.content.height then
- self.content.y = (self.height - self.content.height) // 2
- else
- self.content.y = math.min(0, math.max(-(self.content.height - self.height), self.content.y))
- end
+ end
+ if self.center_content then
+ if self.width > self.content.width then
+ self.content.x = (self.width - self.content.width) // 2
+ else
+ self.content.x = math.min(0, math.max(-(self.content.width - self.width), self.content.x))
+ end
+ if self.height > self.content.height then
+ self.content.y = (self.height - self.content.height) // 2
+ else
+ self.content.y = math.min(0, math.max(-(self.content.height - self.height), self.content.y))
end
end
- if self.parent and (self.width ~= ow or self.height ~= oh) then
- self.parent:child_resized(self)
+ end
+
+ function box:resize_child(c, w, h)
+ assert(c == self.content)
+ if self.constrain_size then
+ self:resize(w, h)
+ elseif self.content:can_resize(w, h) then
+ self.content:do_resize(w, h)
+ self:do_resize(self.width, self.height)
+ self:redraw(0, 0, self.width, self.height, self)
end
end
@@ -390,7 +447,7 @@ function tk.box(a, b)
if x then self.content.x = math.min(0, math.max(-(self.content.width - self.width), x)) end
if y then self.content.y = math.min(0, math.max(-(self.content.height - self.height), y)) end
if self.parent and (self.content.x ~= ox or self.content.y ~= oy) then
- self.parent:child_resized(self)
+ self:redraw(0, 0, self.width, self.height, self)
end
end
@@ -402,15 +459,15 @@ function tk.box(a, b)
return barsize, barpos, baravail
end
- function box:redraw(x0, y0, buf)
+ function box:draw(x0, y0, buf)
local csz = self.control_size
- local acceptable_surface = region_inter({x = x0, y = y0, w = buf:width(), h = buf:height()},
+ local s = region_inter({x = x0, y = y0, w = buf:width(), h = buf:height()},
{x = self.content.x, y = self.content.y,
w = self.content.width, h = self.content.height})
- for _, s in pairs(acceptable_surface) do
+ if s then
local buf2 = buf:sub(s.x - x0, s.y - y0, s.w, s.h)
- self.content:redraw(s.x - self.content.x, s.y - self.content.y, buf2)
+ self.content:draw(s.x - self.content.x, s.y - self.content.y, buf2)
end
local background_surface = region_diff({x = x0, y = y0, w = buf:width(), h = buf:height()},
{x = self.content.x, y = self.content.y,
@@ -502,6 +559,154 @@ function tk.box(a, b)
end
+function tk.grid(a, b)
+ local contents = b or a
+ local opts = b and a or {}
+
+ opts.border_size = 0
+ opts.border_color = tk.rgb(200, 200, 200)
+
+ local grid = tk.widget(nil, nil, opts)
+ grid.contents = contents
+
+ function grid:can_resize(w, h)
+ return w == self.col_pos[#self.contents[1]+1] and h == self.line_pos[#self.contents+1]
+ end
+
+ function grid:reposition_elements()
+ self.line_height = {}
+ self.col_width = {}
+ self.line_pos = {0}
+ self.col_pos = {0}
+ -- Recalculate line height
+ for l = 1, #self.contents do
+ self.line_height[l] = 0
+ for c = 1, #self.contents[l] do
+ self.line_height[l] = math.max(self.contents[l][c].height, self.line_height[l])
+ end
+ self.line_pos[l + 1] = self.line_pos[l] + self.line_height[l] + self.border_size
+ end
+ -- Recalculate column height
+ for c = 1, #self.contents[1] do
+ self.col_width[c] = 0
+ for l = 1, #self.contents do
+ self.col_width[c] = math.max(self.contents[l][c].width, self.col_width[c])
+ end
+ self.col_pos[c + 1] = self.col_pos[c] + self.col_width[c] + self.border_size
+ end
+ -- Reposition elements
+ for l = 1, #self.contents do
+ for c = 1, #self.contents[l] do
+ self.contents[l][c].parent = self
+ self.contents[l][c].grid_l = l
+ self.contents[l][c].grid_c = c
+ self.contents[l][c].x = self.col_pos[c]
+ self.contents[l][c].y = self.line_pos[l]
+ self.contents[l][c]:do_resize(self.col_width[c], self.line_height[l])
+ end
+ end
+ -- Resize me
+ self:do_resize(self.col_pos[#self.contents[1]+1], self.line_pos[#self.contents+1])
+ end
+
+ function grid:get_lc(x, y)
+ function dichotomy(tab, val, i0, i1)
+ if i0 == i1 then
+ return i0
+ elseif i1 == i0 + 1 then
+ if tab[i1] <= val then
+ return i1
+ else
+ return i0
+ end
+ else
+ local mid = (i0 + i1) // 2
+ if tab[mid] <= val then
+ return dichotomy(tab, val, mid, i1)
+ else
+ return dichotomy(tab, val, i0, mid - 1)
+ end
+ end
+ end
+ return dichotomy(self.line_pos, y, 1, #self.contents),
+ dichotomy(self.col_pos, x, 1, #self.contents[1])
+ end
+
+ function grid:draw(x0, y0, buf)
+ local l0, c0 = self:get_lc(x0, y0)
+ local l1, c1 = self:get_lc(x0 + buf:width(), y0 + buf:height())
+
+ for l = l0, l1 do
+ for c = c0, c1 do
+ self:draw_sub(x0, y0, buf, self.contents[l][c])
+ end
+ end
+ end
+
+ function grid:resize_child(item, width, height)
+ if not item:can_resize(width, height) then return end
+
+ local l, c = item.grid_l, item.grid_c
+ local recalc = false
+ if width and width ~= self.col_width[c] then
+ for l1 = 1, #self.contents do
+ self.contents[l1][c]:do_resize(width, nil)
+ end
+ recalc = true
+ end
+ if height and height ~= self.line_height[l] then
+ for c1 = 1, #self.contents[l] do
+ self.contents[l][c1]:do_resize(nil, height)
+ end
+ recalc = true
+ end
+ if recalc then
+ self:reposition_elements()
+ end
+ self:resize(self.width, self.height)
+ end
+
+ function grid:on_mouse_down(x, y, lb, rb, mb)
+ local l, c = self:get_lc(x, y)
+ local it = self.contents[l][c]
+
+ self.mouse_lb = self.mouse_lb or lb
+ self.mouse_rb = self.mouse_rb or rb
+ self.mouse_mb = self.mouse_mb or mb
+
+ if self.mouse_on then
+ it = self.mouse_on
+ else
+ self.mouse_on = it
+ end
+ it:on_mouse_down(x - it.x, y - it.y, lb, mb, rb)
+ end
+
+ function grid:on_mouse_up(x, y, lb, rb, mb)
+ local l, c = self:get_lc(x, y)
+ local it = self.mouse_on or self.contents[l][c]
+ it:on_mouse_up(x - it.x, y - it.y, lb, mb, rb)
+
+ self.mouse_lb = self.mouse_lb and not lb
+ self.mouse_rb = self.mouse_rb and not rb
+ self.mouse_mb = self.mouse_mb and not mb
+ if not (self.mouse_lb or self.mouse_mb or self.mouse_rb) then
+ self.mouse_on = nil
+ end
+ end
+
+ function grid:on_mouse_move(prev_x, prev_y, new_x, new_y)
+ local l, c = self:get_lc(prev_x, prev_y)
+ local it = self.mouse_on or self.contents[l][c]
+ it:on_mouse_move(prev_x - it.x, prev_y - it.y, new_x - it.x, new_y - it.y)
+ end
+
+ grid:reposition_elements()
+
+ return grid
+end
+
+
-- #### #### WINDOW MANAGER WIDGET #### ####
function tk.window_manager()
@@ -529,32 +734,28 @@ function tk.window_manager()
content.x = 1
content.y = 21
- function content:get_draw_buffer(x0, y0, w, h)
- if x0 + w > self.width then w = self.width - x0 end
- if y0 + h > self.height then h = self.height - y0 end
- return win:get_draw_buffer(x0 + 1, y0 + 21, w, h)
- end
+ function win:resize_child(c, w, h)
+ assert(c == self.content)
- function win:get_draw_buffer(x0, y0, w, h)
- -- TODO clipping etc
- end
+ if c:can_resize(w, h) then
+ local reg1 = wm.window_pos[self]
- function win:child_resized(c)
- assert(c == self.content)
+ c:do_resize(w, h)
+ self.width = c.width + 2
+ self.height = c.height + 22
+
+ wm.window_pos[self] = {x = self.x, y = self.y, w = self.width, h = self.height}
+ local reg2 = wm.window_pos[self]
- local reg1 = wm.window_pos[self]
- self.width = c.width + 2
- self.height = c.height + 22
- wm.window_pos[self] = {x = self.x, y = self.y, w = self.width, h = self.height}
- local reg2 = wm.window_pos[self]
- local pieces = region_union(reg1, reg2)
- for _, p in pairs(pieces) do
- wm:redraw_region(p.x, p.y, p.w, p.h)
+ local pieces = region_union(reg1, reg2)
+ for _, p in pairs(pieces) do
+ wm:redraw(p.x, p.y, p.w, p.h)
+ end
end
end
- function win:redraw(x0, y0, buf)
- self:redraw_sub(x0, y0, buf, self.content)
+ function win:draw(x0, y0, buf)
+ self:draw_sub(x0, y0, buf, self.content)
buf:rect(-x0, -y0, self.width, self.height, buf:rgb(255, 128, 0))
buf:fillrect(-x0+1, -y0+1, win.width-2, 20, buf:rgb(200, 200, 200))
@@ -588,7 +789,7 @@ function tk.window_manager()
local reg2 = wm.window_pos[self]
local pieces = region_union(reg1, reg2)
for _, p in pairs(pieces) do
- wm:redraw_region(p.x, p.y, p.w, p.h)
+ wm:redraw(p.x, p.y, p.w, p.h)
end
else
self.content:on_mouse_move(px-1, py-21, nx-1, ny-21)
@@ -609,29 +810,21 @@ function tk.window_manager()
for i, w2 in pairs(self.windows) do
if w2 == win then
table.remove(self.windows, i)
- return
+ break
end
end
- end
+ self.window_pos[win] = nil
- function wm:redraw_region(x, y, w, h)
- local ok = region_inter({x = 0, y = 0, w = self.width, h = self.height},
- {x = x, y = y, w = w, h = h})
- for _, r in pairs(ok) do
- self:redraw(r.x, r.y, self:get_draw_buffer(r.x, r.y, r.w, r.h))
- self:finalize_draw_buffer()
- end
+ self:redraw_win(win)
end
function wm:redraw_win(win)
- self:redraw_region(win.x, win.y, win.width, win.height)
+ self:redraw(win.x, win.y, win.width, win.height)
end
- function wm:redraw(x0, y0, buf)
+ function wm:draw(x0, y0, buf)
local remaining = { {x = x0, y = y0, w = buf:width(), h = buf:height()} }
- -- TODO do this in inverse order and clip regions of hidden windows
- -- so that less redrawing is done
for i = #self.windows, 1, -1 do
win = self.windows[i]
if win.visible then
@@ -640,8 +833,8 @@ function tk.window_manager()
for _, reg in pairs(remaining) do
local draw_to = region_inter(reg, win_reg)
- for _, reg2 in pairs(draw_to) do
- win:redraw(reg2.x - win.x, reg2.y - win.y, buf:sub(reg2.x - x0, reg2.y - y0, reg2.w, reg2.h))
+ if draw_to then
+ win:draw(draw_to.x - win.x, draw_to.y - win.y, buf:sub(draw_to.x - x0, draw_to.y - y0, draw_to.w, draw_to.h))
end
local remain_to = region_diff(reg, win_reg)