aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/core/emptyview.lua40
-rw-r--r--data/core/node.lua737
-rw-r--r--data/core/rootview.lua773
3 files changed, 779 insertions, 771 deletions
diff --git a/data/core/emptyview.lua b/data/core/emptyview.lua
new file mode 100644
index 00000000..9d375c20
--- /dev/null
+++ b/data/core/emptyview.lua
@@ -0,0 +1,40 @@
+local style = require "core.style"
+local keymap = require "core.keymap"
+local View = require "core.view"
+
+local EmptyView = View:extend()
+
+local function draw_text(x, y, color)
+ local th = style.big_font:get_height()
+ local dh = 2 * th + style.padding.y * 2
+ local x1, y1 = x, y + (dh - th) / 2
+ x = renderer.draw_text(style.big_font, "Lite XL", x1, y1, color)
+ renderer.draw_text(style.font, "version " .. VERSION, x1, y1 + th, color)
+ x = x + style.padding.x
+ renderer.draw_rect(x, y, math.ceil(1 * SCALE), dh, color)
+ local lines = {
+ { fmt = "%s to run a command", cmd = "core:find-command" },
+ { fmt = "%s to open a file from the project", cmd = "core:find-file" },
+ { fmt = "%s to change project folder", cmd = "core:change-project-folder" },
+ { fmt = "%s to open a project folder", cmd = "core:open-project-folder" },
+ }
+ th = style.font:get_height()
+ y = y + (dh - (th + style.padding.y) * #lines) / 2
+ local w = 0
+ for _, line in ipairs(lines) do
+ local text = string.format(line.fmt, keymap.get_binding(line.cmd))
+ w = math.max(w, renderer.draw_text(style.font, text, x + style.padding.x, y, color))
+ y = y + th + style.padding.y
+ end
+ return w, dh
+end
+
+function EmptyView:draw()
+ self:draw_background(style.background)
+ local w, h = draw_text(0, 0, { 0, 0, 0, 0 })
+ local x = self.position.x + math.max(style.padding.x, (self.size.x - w) / 2)
+ local y = self.position.y + (self.size.y - h) / 2
+ draw_text(x, y, style.dim)
+end
+
+return EmptyView
diff --git a/data/core/node.lua b/data/core/node.lua
new file mode 100644
index 00000000..bced052d
--- /dev/null
+++ b/data/core/node.lua
@@ -0,0 +1,737 @@
+local core = require "core"
+local common = require "core.common"
+local config = require "core.config"
+local style = require "core.style"
+local Object = require "core.object"
+local EmptyView = require "core.emptyview"
+local View = require "core.view"
+
+local Node = Object:extend()
+
+function Node:new(type)
+ self.type = type or "leaf"
+ self.position = { x = 0, y = 0 }
+ self.size = { x = 0, y = 0 }
+ self.views = {}
+ self.divider = 0.5
+ if self.type == "leaf" then
+ self:add_view(EmptyView())
+ end
+ self.hovered = {x = -1, y = -1 }
+ self.hovered_close = 0
+ self.tab_shift = 0
+ self.tab_offset = 1
+ self.tab_width = style.tab_width
+ self.move_towards = View.move_towards
+end
+
+
+function Node:propagate(fn, ...)
+ self.a[fn](self.a, ...)
+ self.b[fn](self.b, ...)
+end
+
+
+function Node:on_mouse_moved(x, y, ...)
+ if self.type == "leaf" then
+ self.hovered.x, self.hovered.y = x, y
+ self.active_view:on_mouse_moved(x, y, ...)
+ else
+ self:propagate("on_mouse_moved", x, y, ...)
+ end
+end
+
+
+function Node:on_mouse_released(...)
+ if self.type == "leaf" then
+ self.active_view:on_mouse_released(...)
+ else
+ self:propagate("on_mouse_released", ...)
+ end
+end
+
+
+function Node:consume(node)
+ for k, _ in pairs(self) do self[k] = nil end
+ for k, v in pairs(node) do self[k] = v end
+end
+
+
+local type_map = { up="vsplit", down="vsplit", left="hsplit", right="hsplit" }
+
+-- The "locked" argument below should be in the form {x = <boolean>, y = <boolean>}
+-- and it indicates if the node want to have a fixed size along the axis where the
+-- boolean is true. If not it will be expanded to take all the available space.
+-- The "resizable" flag indicates if, along the "locked" axis the node can be resized
+-- by the user. If the node is marked as resizable their view should provide a
+-- set_target_size method.
+function Node:split(dir, view, locked, resizable)
+ assert(self.type == "leaf", "Tried to split non-leaf node")
+ local node_type = assert(type_map[dir], "Invalid direction")
+ local last_active = core.active_view
+ local child = Node()
+ child:consume(self)
+ self:consume(Node(node_type))
+ self.a = child
+ self.b = Node()
+ if view then self.b:add_view(view) end
+ if locked then
+ assert(type(locked) == 'table')
+ self.b.locked = locked
+ self.b.resizable = resizable or false
+ core.set_active_view(last_active)
+ end
+ if dir == "up" or dir == "left" then
+ self.a, self.b = self.b, self.a
+ return self.a
+ end
+ return self.b
+end
+
+function Node:remove_view(root, view)
+ if #self.views > 1 then
+ local idx = self:get_view_idx(view)
+ if idx < self.tab_offset then
+ self.tab_offset = self.tab_offset - 1
+ end
+ table.remove(self.views, idx)
+ if self.active_view == view then
+ self:set_active_view(self.views[idx] or self.views[#self.views])
+ end
+ else
+ local parent = self:get_parent_node(root)
+ local is_a = (parent.a == self)
+ local other = parent[is_a and "b" or "a"]
+ local locked_size_x, locked_size_y = other:get_locked_size()
+ local locked_size
+ if parent.type == "hsplit" then
+ locked_size = locked_size_x
+ else
+ locked_size = locked_size_y
+ end
+ local next_primary
+ if self.is_primary_node then
+ next_primary = core.root_view:select_next_primary_node()
+ end
+ if locked_size or (self.is_primary_node and not next_primary) then
+ self.views = {}
+ self:add_view(EmptyView())
+ else
+ if other == next_primary then
+ next_primary = parent
+ end
+ parent:consume(other)
+ local p = parent
+ while p.type ~= "leaf" do
+ p = p[is_a and "a" or "b"]
+ end
+ p:set_active_view(p.active_view)
+ if self.is_primary_node then
+ next_primary.is_primary_node = true
+ end
+ end
+ end
+ core.last_active_view = nil
+end
+
+function Node:close_view(root, view)
+ local do_close = function()
+ self:remove_view(root, view)
+ end
+ view:try_close(do_close)
+end
+
+
+function Node:close_active_view(root)
+ self:close_view(root, self.active_view)
+end
+
+
+function Node:add_view(view, idx)
+ assert(self.type == "leaf", "Tried to add view to non-leaf node")
+ assert(not self.locked, "Tried to add view to locked node")
+ if self.views[1] and self.views[1]:is(EmptyView) then
+ table.remove(self.views)
+ end
+ table.insert(self.views, idx or (#self.views + 1), view)
+ self:set_active_view(view)
+end
+
+
+function Node:set_active_view(view)
+ assert(self.type == "leaf", "Tried to set active view on non-leaf node")
+ self.active_view = view
+ core.set_active_view(view)
+end
+
+
+function Node:get_view_idx(view)
+ for i, v in ipairs(self.views) do
+ if v == view then return i end
+ end
+end
+
+
+function Node:get_node_for_view(view)
+ for _, v in ipairs(self.views) do
+ if v == view then return self end
+ end
+ if self.type ~= "leaf" then
+ return self.a:get_node_for_view(view) or self.b:get_node_for_view(view)
+ end
+end
+
+
+function Node:get_parent_node(root)
+ if root.a == self or root.b == self then
+ return root
+ elseif root.type ~= "leaf" then
+ return self:get_parent_node(root.a) or self:get_parent_node(root.b)
+ end
+end
+
+
+function Node:get_children(t)
+ t = t or {}
+ for _, view in ipairs(self.views) do
+ table.insert(t, view)
+ end
+ if self.a then self.a:get_children(t) end
+ if self.b then self.b:get_children(t) end
+ return t
+end
+
+
+-- return the width including the padding space and separately
+-- the padding space itself
+local function get_scroll_button_width()
+ local w = style.icon_font:get_width(">")
+ local pad = w
+ return w + 2 * pad, pad
+end
+
+
+function Node:get_divider_overlapping_point(px, py)
+ if self.type ~= "leaf" then
+ local axis = self.type == "hsplit" and "x" or "y"
+ if self.a:is_resizable(axis) and self.b:is_resizable(axis) then
+ local p = 6
+ local x, y, w, h = self:get_divider_rect()
+ x, y = x - p, y - p
+ w, h = w + p * 2, h + p * 2
+ if px > x and py > y and px < x + w and py < y + h then
+ return self
+ end
+ end
+ return self.a:get_divider_overlapping_point(px, py)
+ or self.b:get_divider_overlapping_point(px, py)
+ end
+end
+
+
+function Node:get_visible_tabs_number()
+ return math.min(#self.views - self.tab_offset + 1, config.max_tabs)
+end
+
+
+function Node:get_tab_overlapping_point(px, py)
+ if not self:should_show_tabs() then return nil end
+ local tabs_number = self:get_visible_tabs_number()
+ local x1, y1, w, h = self:get_tab_rect(self.tab_offset)
+ local x2, y2 = self:get_tab_rect(self.tab_offset + tabs_number)
+ if px >= x1 and py >= y1 and px < x2 and py < y1 + h then
+ return math.floor((px - x1) / w) + self.tab_offset
+ end
+end
+
+
+function Node:should_show_tabs()
+ if self.locked then return false end
+ local dn = core.root_view.dragged_node
+ if #self.views > 1
+ or (dn and dn.dragging) then -- show tabs while dragging
+ return true
+ elseif config.always_show_tabs then
+ return not self.views[1]:is(EmptyView)
+ end
+ return false
+end
+
+
+local function close_button_location(x, w)
+ local cw = style.icon_font:get_width("C")
+ local pad = style.padding.y
+ return x + w - pad - cw, cw, pad
+end
+
+
+function Node:get_scroll_button_index(px, py)
+ if #self.views == 1 then return end
+ for i = 1, 2 do
+ local x, y, w, h = self:get_scroll_button_rect(i)
+ if px >= x and px < x + w and py >= y and py < y + h then
+ return i
+ end
+ end
+end
+
+
+function Node:tab_hovered_update(px, py)
+ local tab_index = self:get_tab_overlapping_point(px, py)
+ self.hovered_tab = tab_index
+ self.hovered_close = 0
+ self.hovered_scroll_button = 0
+ if tab_index then
+ local x, y, w, h = self:get_tab_rect(tab_index)
+ local cx, cw = close_button_location(x, w)
+ if px >= cx and px < cx + cw and py >= y and py < y + h and config.tab_close_button then
+ self.hovered_close = tab_index
+ end
+ else
+ self.hovered_scroll_button = self:get_scroll_button_index(px, py) or 0
+ end
+end
+
+
+function Node:get_child_overlapping_point(x, y)
+ local child
+ if self.type == "leaf" then
+ return self
+ elseif self.type == "hsplit" then
+ child = (x < self.b.position.x) and self.a or self.b
+ elseif self.type == "vsplit" then
+ child = (y < self.b.position.y) and self.a or self.b
+ end
+ return child:get_child_overlapping_point(x, y)
+end
+
+
+function Node:get_scroll_button_rect(index)
+ local w, pad = get_scroll_button_width()
+ local h = style.font:get_height() + style.padding.y * 2
+ local x = self.position.x + (index == 1 and 0 or self.size.x - w)
+ return x, self.position.y, w, h, pad
+end
+
+
+function Node:get_tab_rect(idx)
+ local sbw = get_scroll_button_width()
+ local maxw = self.size.x - 2 * sbw
+ local x0 = self.position.x + sbw
+ local x1 = x0 + common.clamp(self.tab_width * (idx - 1) - self.tab_shift, 0, maxw)
+ local x2 = x0 + common.clamp(self.tab_width * idx - self.tab_shift, 0, maxw)
+ local h = style.font:get_height() + style.padding.y * 2
+ return x1, self.position.y, x2 - x1, h
+end
+
+
+function Node:get_divider_rect()
+ local x, y = self.position.x, self.position.y
+ if self.type == "hsplit" then
+ return x + self.a.size.x, y, style.divider_size, self.size.y
+ elseif self.type == "vsplit" then
+ return x, y + self.a.size.y, self.size.x, style.divider_size
+ end
+end
+
+
+-- Return two values for x and y axis and each of them is either falsy or a number.
+-- A falsy value indicate no fixed size along the corresponding direction.
+function Node:get_locked_size()
+ if self.type == "leaf" then
+ if self.locked then
+ local size = self.active_view.size
+ -- The values below should be either a falsy value or a number
+ local sx = (self.locked and self.locked.x) and size.x
+ local sy = (self.locked and self.locked.y) and size.y
+ return sx, sy
+ end
+ else
+ local x1, y1 = self.a:get_locked_size()
+ local x2, y2 = self.b:get_locked_size()
+ -- The values below should be either a falsy value or a number
+ local sx, sy
+ if self.type == 'hsplit' then
+ if x1 and x2 then
+ local dsx = (x1 < 1 or x2 < 1) and 0 or style.divider_size
+ sx = x1 + x2 + dsx
+ end
+ sy = y1 or y2
+ else
+ if y1 and y2 then
+ local dsy = (y1 < 1 or y2 < 1) and 0 or style.divider_size
+ sy = y1 + y2 + dsy
+ end
+ sx = x1 or x2
+ end
+ return sx, sy
+ end
+end
+
+
+function Node.copy_position_and_size(dst, src)
+ dst.position.x, dst.position.y = src.position.x, src.position.y
+ dst.size.x, dst.size.y = src.size.x, src.size.y
+end
+
+
+-- calculating the sizes is the same for hsplits and vsplits, except the x/y
+-- axis are swapped; this function lets us use the same code for both
+local function calc_split_sizes(self, x, y, x1, x2, y1, y2)
+ local ds = ((x1 and x1 < 1) or (x2 and x2 < 1)) and 0 or style.divider_size
+ local n = x1 and x1 + ds or (x2 and self.size[x] - x2 or math.floor(self.size[x] * self.divider))
+ self.a.position[x] = self.position[x]
+ self.a.position[y] = self.position[y]
+ self.a.size[x] = n - ds
+ self.a.size[y] = self.size[y]
+ self.b.position[x] = self.position[x] + n
+ self.b.position[y] = self.position[y]
+ self.b.size[x] = self.size[x] - n
+ self.b.size[y] = self.size[y]
+end
+
+
+function Node:update_layout()
+ if self.type == "leaf" then
+ local av = self.active_view
+ if self:should_show_tabs() then
+ local _, _, _, th = self:get_tab_rect(1)
+ av.position.x, av.position.y = self.position.x, self.position.y + th
+ av.size.x, av.size.y = self.size.x, self.size.y - th
+ else
+ Node.copy_position_and_size(av, self)
+ end
+ else
+ local x1, y1 = self.a:get_locked_size()
+ local x2, y2 = self.b:get_locked_size()
+ if self.type == "hsplit" then
+ calc_split_sizes(self, "x", "y", x1, x2)
+ elseif self.type == "vsplit" then
+ calc_split_sizes(self, "y", "x", y1, y2)
+ end
+ self.a:update_layout()
+ self.b:update_layout()
+ end
+end
+
+
+function Node:scroll_tabs_to_visible()
+ local index = self:get_view_idx(self.active_view)
+ if index then
+ local tabs_number = self:get_visible_tabs_number()
+ if self.tab_offset > index then
+ self.tab_offset = index
+ elseif self.tab_offset + tabs_number - 1 < index then
+ self.tab_offset = index - tabs_number + 1
+ elseif tabs_number < config.max_tabs and self.tab_offset > 1 then
+ self.tab_offset = #self.views - config.max_tabs + 1
+ end
+ end
+end
+
+
+function Node:scroll_tabs(dir)
+ local view_index = self:get_view_idx(self.active_view)
+ if dir == 1 then
+ if self.tab_offset > 1 then
+ self.tab_offset = self.tab_offset - 1
+ local last_index = self.tab_offset + self:get_visible_tabs_number() - 1
+ if view_index > last_index then
+ self:set_active_view(self.views[last_index])
+ end
+ end
+ elseif dir == 2 then
+ local tabs_number = self:get_visible_tabs_number()
+ if self.tab_offset + tabs_number - 1 < #self.views then
+ self.tab_offset = self.tab_offset + 1
+ local view_index = self:get_view_idx(self.active_view)
+ if view_index < self.tab_offset then
+ self:set_active_view(self.views[self.tab_offset])
+ end
+ end
+ end
+end
+
+
+function Node:target_tab_width()
+ local n = self:get_visible_tabs_number()
+ local w = self.size.x - get_scroll_button_width() * 2
+ return common.clamp(style.tab_width, w / config.max_tabs, w / n)
+end
+
+
+function Node:update()
+ if self.type == "leaf" then
+ self:scroll_tabs_to_visible()
+ for _, view in ipairs(self.views) do
+ view:update()
+ end
+ self:tab_hovered_update(self.hovered.x, self.hovered.y)
+ local tab_width = self:target_tab_width()
+ self:move_towards("tab_shift", tab_width * (self.tab_offset - 1))
+ self:move_towards("tab_width", tab_width)
+ else
+ self.a:update()
+ self.b:update()
+ end
+end
+
+function Node:draw_tab(text, is_active, is_hovered, is_close_hovered, x, y, w, h, standalone)
+ local ds = style.divider_size
+ local dots_width = style.font:get_width("…")
+ local color = style.dim
+ local padding_y = style.padding.y
+ renderer.draw_rect(x + w, y + padding_y, ds, h - padding_y * 2, style.dim)
+ if standalone then
+ renderer.draw_rect(x-1, y-1, w+2, h+2, style.background2)
+ end
+ if is_active then
+ color = style.text
+ renderer.draw_rect(x, y, w, h, style.background)
+ renderer.draw_rect(x + w, y, ds, h, style.divider)
+ renderer.draw_rect(x - ds, y, ds, h, style.divider)
+ end
+ local cx, cw, cspace = close_button_location(x, w)
+ local show_close_button = ((is_active or is_hovered) and not standalone and config.tab_close_button)
+ if show_close_button then
+ local close_style = is_close_hovered and style.text or style.dim
+ common.draw_text(style.icon_font, close_style, "C", nil, cx, y, 0, h)
+ end
+ if is_hovered then
+ color = style.text
+ end
+ local padx = style.padding.x
+ -- Normally we should substract "cspace" from text_avail_width and from the
+ -- clipping width. It is the padding space we give to the left and right of the
+ -- close button. However, since we are using dots to terminate filenames, we
+ -- choose to ignore "cspace" accepting that the text can possibly "touch" the
+ -- close button.
+ local text_avail_width = cx - x - padx
+ core.push_clip_rect(x, y, cx - x, h)
+ x, w = x + padx, w - padx * 2
+ local align = "center"
+ if style.font:get_width(text) > text_avail_width then
+ align = "left"
+ for i = 1, #text do
+ local reduced_text = text:sub(1, #text - i)
+ if style.font:get_width(reduced_text) + dots_width <= text_avail_width then
+ text = reduced_text .. "…"
+ break
+ end
+ end
+ end
+ common.draw_text(style.font, color, text, align, x, y, w, h)
+ core.pop_clip_rect()
+end
+
+function Node:draw_tabs()
+ local x, y, w, h, scroll_padding = self:get_scroll_button_rect(1)
+ local ds = style.divider_size
+ local dots_width = style.font:get_width("…")
+ core.push_clip_rect(x, y, self.size.x, h)
+ renderer.draw_rect(x, y, self.size.x, h, style.background2)
+ renderer.draw_rect(x, y + h - ds, self.size.x, ds, style.divider)
+
+ if self.tab_offset > 1 then
+ local button_style = self.hovered_scroll_button == 1 and style.text or style.dim
+ common.draw_text(style.icon_font, button_style, "<", nil, x + scroll_padding, y, 0, h)
+ end
+
+ local tabs_number = self:get_visible_tabs_number()
+ if #self.views > self.tab_offset + tabs_number - 1 then
+ local xrb, yrb, wrb = self:get_scroll_button_rect(2)
+ local button_style = self.hovered_scroll_button == 2 and style.text or style.dim
+ common.draw_text(style.icon_font, button_style, ">", nil, xrb + scroll_padding, yrb, 0, h)
+ end
+
+ for i = self.tab_offset, self.tab_offset + tabs_number - 1 do
+ local view = self.views[i]
+ local x, y, w, h = self:get_tab_rect(i)
+ self:draw_tab(view:get_name(), view == self.active_view,
+ i == self.hovered_tab, i == self.hovered_close,
+ x, y, w, h)
+ end
+
+ core.pop_clip_rect()
+end
+
+
+function Node:draw()
+ if self.type == "leaf" then
+ if self:should_show_tabs() then
+ self:draw_tabs()
+ end
+ local pos, size = self.active_view.position, self.active_view.size
+ core.push_clip_rect(pos.x, pos.y, size.x, size.y)
+ self.active_view:draw()
+ core.pop_clip_rect()
+ else
+ local x, y, w, h = self:get_divider_rect()
+ renderer.draw_rect(x, y, w, h, style.divider)
+ self:propagate("draw")
+ end
+end
+
+
+function Node:is_empty()
+ if self.type == "leaf" then
+ return #self.views == 0 or (#self.views == 1 and self.views[1]:is(EmptyView))
+ else
+ return self.a:is_empty() and self.b:is_empty()
+ end
+end
+
+
+function Node:close_all_docviews(keep_active)
+ local node_active_view = self.active_view
+ local lost_active_view = false
+ if self.type == "leaf" then
+ local i = 1
+ while i <= #self.views do
+ local view = self.views[i]
+ if view.context == "session" and (not keep_active or view ~= self.active_view) then
+ table.remove(self.views, i)
+ if view == node_active_view then
+ lost_active_view = true
+ end
+ else
+ i = i + 1
+ end
+ end
+ self.tab_offset = 1
+ if #self.views == 0 and self.is_primary_node then
+ -- if we are not the primary view and we had the active view it doesn't
+ -- matter to reattribute the active view because, within the close_all_docviews
+ -- top call, the primary node will take the active view anyway.
+ -- Set the empty view and takes the active view.
+ self:add_view(EmptyView())
+ elseif #self.views > 0 and lost_active_view then
+ -- In practice we never get there but if a view remain we need
+ -- to reset the Node's active view.
+ self:set_active_view(self.views[1])
+ end
+ else
+ self.a:close_all_docviews(keep_active)
+ self.b:close_all_docviews(keep_active)
+ if self.a:is_empty() and not self.a.is_primary_node then
+ self:consume(self.b)
+ elseif self.b:is_empty() and not self.b.is_primary_node then
+ self:consume(self.a)
+ end
+ end
+end
+
+-- Returns true for nodes that accept either "proportional" resizes (based on the
+-- node.divider) or "locked" resizable nodes (along the resize axis).
+function Node:is_resizable(axis)
+ if self.type == 'leaf' then
+ return not self.locked or not self.locked[axis] or self.resizable
+ else
+ local a_resizable = self.a:is_resizable(axis)
+ local b_resizable = self.b:is_resizable(axis)
+ return a_resizable and b_resizable
+ end
+end
+
+
+-- Return true iff it is a locked pane along the rezise axis and is
+-- declared "resizable".
+function Node:is_locked_resizable(axis)
+ return self.locked and self.locked[axis] and self.resizable
+end
+
+
+function Node:resize(axis, value)
+ -- the application works fine with non-integer values but to have pixel-perfect
+ -- placements of view elements, like the scrollbar, we round the value to be
+ -- an integer.
+ value = math.floor(value)
+ if self.type == 'leaf' then
+ -- If it is not locked we don't accept the
+ -- resize operation here because for proportional panes the resize is
+ -- done using the "divider" value of the parent node.
+ if self:is_locked_resizable(axis) then
+ return self.active_view:set_target_size(axis, value)
+ end
+ else
+ if self.type == (axis == "x" and "hsplit" or "vsplit") then
+ -- we are resizing a node that is splitted along the resize axis
+ if self.a:is_locked_resizable(axis) and self.b:is_locked_resizable(axis) then
+ local rem_value = value - self.a.size[axis]
+ if rem_value >= 0 then
+ return self.b.active_view:set_target_size(axis, rem_value)
+ else
+ self.b.active_view:set_target_size(axis, 0)
+ return self.a.active_view:set_target_size(axis, value)
+ end
+ end
+ else
+ -- we are resizing a node that is splitted along the axis perpendicular
+ -- to the resize axis
+ local a_resizable = self.a:is_resizable(axis)
+ local b_resizable = self.b:is_resizable(axis)
+ if a_resizable and b_resizable then
+ self.a:resize(axis, value)
+ self.b:resize(axis, value)
+ end
+ end
+ end
+end
+
+
+function Node:get_split_type(mouse_x, mouse_y)
+ local x, y = self.position.x, self.position.y
+ local w, h = self.size.x, self.size.y
+ local _, _, _, tab_h = self:get_scroll_button_rect(1)
+ y = y + tab_h
+ h = h - tab_h
+
+ local local_mouse_x = mouse_x - x
+ local local_mouse_y = mouse_y - y
+
+ if local_mouse_y < 0 then
+ return "tab"
+ else
+ local left_pct = local_mouse_x * 100 / w
+ local top_pct = local_mouse_y * 100 / h
+ if left_pct <= 30 then
+ return "left"
+ elseif left_pct >= 70 then
+ return "right"
+ elseif top_pct <= 30 then
+ return "up"
+ elseif top_pct >= 70 then
+ return "down"
+ end
+ return "middle"
+ end
+end
+
+
+function Node:get_drag_overlay_tab_position(x, y, dragged_node, dragged_index)
+ local tab_index = self:get_tab_overlapping_point(x, y)
+ if not tab_index then
+ local first_tab_x = self:get_tab_rect(1)
+ if x < first_tab_x then
+ -- mouse before first visible tab
+ tab_index = self.tab_offset or 1
+ else
+ -- mouse after last visible tab
+ tab_index = self:get_visible_tabs_number() + (self.tab_offset - 1 or 0)
+ end
+ end
+ local tab_x, tab_y, tab_w, tab_h = self:get_tab_rect(tab_index)
+ if x > tab_x + tab_w / 2 and tab_index <= #self.views then
+ -- use next tab
+ tab_x = tab_x + tab_w
+ tab_index = tab_index + 1
+ end
+ if self == dragged_node and dragged_index and tab_index > dragged_index then
+ -- the tab we are moving is counted in tab_index
+ tab_index = tab_index - 1
+ tab_x = tab_x - tab_w
+ end
+ return tab_index, tab_x, tab_y, tab_w, tab_h
+end
+
+return Node
diff --git a/data/core/rootview.lua b/data/core/rootview.lua
index 97caca1c..fb735fe3 100644
--- a/data/core/rootview.lua
+++ b/data/core/rootview.lua
@@ -1,780 +1,11 @@
local core = require "core"
local common = require "core.common"
-local config = require "core.config"
local style = require "core.style"
-local keymap = require "core.keymap"
-local Object = require "core.object"
+local Node = require "core.node"
local View = require "core.view"
-local NagView = require "core.nagview"
local DocView = require "core.docview"
-local EmptyView = View:extend()
-
-local function draw_text(x, y, color)
- local th = style.big_font:get_height()
- local dh = 2 * th + style.padding.y * 2
- local x1, y1 = x, y + (dh - th) / 2
- x = renderer.draw_text(style.big_font, "Lite XL", x1, y1, color)
- renderer.draw_text(style.font, "version " .. VERSION, x1, y1 + th, color)
- x = x + style.padding.x
- renderer.draw_rect(x, y, math.ceil(1 * SCALE), dh, color)
- local lines = {
- { fmt = "%s to run a command", cmd = "core:find-command" },
- { fmt = "%s to open a file from the project", cmd = "core:find-file" },
- { fmt = "%s to change project folder", cmd = "core:change-project-folder" },
- { fmt = "%s to open a project folder", cmd = "core:open-project-folder" },
- }
- th = style.font:get_height()
- y = y + (dh - (th + style.padding.y) * #lines) / 2
- local w = 0
- for _, line in ipairs(lines) do
- local text = string.format(line.fmt, keymap.get_binding(line.cmd))
- w = math.max(w, renderer.draw_text(style.font, text, x + style.padding.x, y, color))
- y = y + th + style.padding.y
- end
- return w, dh
-end
-
-function EmptyView:draw()
- self:draw_background(style.background)
- local w, h = draw_text(0, 0, { 0, 0, 0, 0 })
- local x = self.position.x + math.max(style.padding.x, (self.size.x - w) / 2)
- local y = self.position.y + (self.size.y - h) / 2
- draw_text(x, y, style.dim)
-end
-
-
-
-local Node = Object:extend()
-
-function Node:new(type)
- self.type = type or "leaf"
- self.position = { x = 0, y = 0 }
- self.size = { x = 0, y = 0 }
- self.views = {}
- self.divider = 0.5
- if self.type == "leaf" then
- self:add_view(EmptyView())
- end
- self.hovered = {x = -1, y = -1 }
- self.hovered_close = 0
- self.tab_shift = 0
- self.tab_offset = 1
- self.tab_width = style.tab_width
- self.move_towards = View.move_towards
-end
-
-
-function Node:propagate(fn, ...)
- self.a[fn](self.a, ...)
- self.b[fn](self.b, ...)
-end
-
-
-function Node:on_mouse_moved(x, y, ...)
- if self.type == "leaf" then
- self.hovered.x, self.hovered.y = x, y
- self.active_view:on_mouse_moved(x, y, ...)
- else
- self:propagate("on_mouse_moved", x, y, ...)
- end
-end
-
-
-function Node:on_mouse_released(...)
- if self.type == "leaf" then
- self.active_view:on_mouse_released(...)
- else
- self:propagate("on_mouse_released", ...)
- end
-end
-
-
-function Node:consume(node)
- for k, _ in pairs(self) do self[k] = nil end
- for k, v in pairs(node) do self[k] = v end
-end
-
-
-local type_map = { up="vsplit", down="vsplit", left="hsplit", right="hsplit" }
-
--- The "locked" argument below should be in the form {x = <boolean>, y = <boolean>}
--- and it indicates if the node want to have a fixed size along the axis where the
--- boolean is true. If not it will be expanded to take all the available space.
--- The "resizable" flag indicates if, along the "locked" axis the node can be resized
--- by the user. If the node is marked as resizable their view should provide a
--- set_target_size method.
-function Node:split(dir, view, locked, resizable)
- assert(self.type == "leaf", "Tried to split non-leaf node")
- local node_type = assert(type_map[dir], "Invalid direction")
- local last_active = core.active_view
- local child = Node()
- child:consume(self)
- self:consume(Node(node_type))
- self.a = child
- self.b = Node()
- if view then self.b:add_view(view) end
- if locked then
- assert(type(locked) == 'table')
- self.b.locked = locked
- self.b.resizable = resizable or false
- core.set_active_view(last_active)
- end
- if dir == "up" or dir == "left" then
- self.a, self.b = self.b, self.a
- return self.a
- end
- return self.b
-end
-
-function Node:remove_view(root, view)
- if #self.views > 1 then
- local idx = self:get_view_idx(view)
- if idx < self.tab_offset then
- self.tab_offset = self.tab_offset - 1
- end
- table.remove(self.views, idx)
- if self.active_view == view then
- self:set_active_view(self.views[idx] or self.views[#self.views])
- end
- else
- local parent = self:get_parent_node(root)
- local is_a = (parent.a == self)
- local other = parent[is_a and "b" or "a"]
- local locked_size_x, locked_size_y = other:get_locked_size()
- local locked_size
- if parent.type == "hsplit" then
- locked_size = locked_size_x
- else
- locked_size = locked_size_y
- end
- local next_primary
- if self.is_primary_node then
- next_primary = core.root_view:select_next_primary_node()
- end
- if locked_size or (self.is_primary_node and not next_primary) then
- self.views = {}
- self:add_view(EmptyView())
- else
- if other == next_primary then
- next_primary = parent
- end
- parent:consume(other)
- local p = parent
- while p.type ~= "leaf" do
- p = p[is_a and "a" or "b"]
- end
- p:set_active_view(p.active_view)
- if self.is_primary_node then
- next_primary.is_primary_node = true
- end
- end
- end
- core.last_active_view = nil
-end
-
-function Node:close_view(root, view)
- local do_close = function()
- self:remove_view(root, view)
- end
- view:try_close(do_close)
-end
-
-
-function Node:close_active_view(root)
- self:close_view(root, self.active_view)
-end
-
-
-function Node:add_view(view, idx)
- assert(self.type == "leaf", "Tried to add view to non-leaf node")
- assert(not self.locked, "Tried to add view to locked node")
- if self.views[1] and self.views[1]:is(EmptyView) then
- table.remove(self.views)
- end
- table.insert(self.views, idx or (#self.views + 1), view)
- self:set_active_view(view)
-end
-
-
-function Node:set_active_view(view)
- assert(self.type == "leaf", "Tried to set active view on non-leaf node")
- self.active_view = view
- core.set_active_view(view)
-end
-
-
-function Node:get_view_idx(view)
- for i, v in ipairs(self.views) do
- if v == view then return i end
- end
-end
-
-
-function Node:get_node_for_view(view)
- for _, v in ipairs(self.views) do
- if v == view then return self end
- end
- if self.type ~= "leaf" then
- return self.a:get_node_for_view(view) or self.b:get_node_for_view(view)
- end
-end
-
-
-function Node:get_parent_node(root)
- if root.a == self or root.b == self then
- return root
- elseif root.type ~= "leaf" then
- return self:get_parent_node(root.a) or self:get_parent_node(root.b)
- end
-end
-
-
-function Node:get_children(t)
- t = t or {}
- for _, view in ipairs(self.views) do
- table.insert(t, view)
- end
- if self.a then self.a:get_children(t) end
- if self.b then self.b:get_children(t) end
- return t
-end
-
-
--- return the width including the padding space and separately
--- the padding space itself
-local function get_scroll_button_width()
- local w = style.icon_font:get_width(">")
- local pad = w
- return w + 2 * pad, pad
-end
-
-
-function Node:get_divider_overlapping_point(px, py)
- if self.type ~= "leaf" then
- local axis = self.type == "hsplit" and "x" or "y"
- if self.a:is_resizable(axis) and self.b:is_resizable(axis) then
- local p = 6
- local x, y, w, h = self:get_divider_rect()
- x, y = x - p, y - p
- w, h = w + p * 2, h + p * 2
- if px > x and py > y and px < x + w and py < y + h then
- return self
- end
- end
- return self.a:get_divider_overlapping_point(px, py)
- or self.b:get_divider_overlapping_point(px, py)
- end
-end
-
-
-function Node:get_visible_tabs_number()
- return math.min(#self.views - self.tab_offset + 1, config.max_tabs)
-end
-
-
-function Node:get_tab_overlapping_point(px, py)
- if not self:should_show_tabs() then return nil end
- local tabs_number = self:get_visible_tabs_number()
- local x1, y1, w, h = self:get_tab_rect(self.tab_offset)
- local x2, y2 = self:get_tab_rect(self.tab_offset + tabs_number)
- if px >= x1 and py >= y1 and px < x2 and py < y1 + h then
- return math.floor((px - x1) / w) + self.tab_offset
- end
-end
-
-
-function Node:should_show_tabs()
- if self.locked then return false end
- local dn = core.root_view.dragged_node
- if #self.views > 1
- or (dn and dn.dragging) then -- show tabs while dragging
- return true
- elseif config.always_show_tabs then
- return not self.views[1]:is(EmptyView)
- end
- return false
-end
-
-
-local function close_button_location(x, w)
- local cw = style.icon_font:get_width("C")
- local pad = style.padding.y
- return x + w - pad - cw, cw, pad
-end
-
-
-function Node:get_scroll_button_index(px, py)
- if #self.views == 1 then return end
- for i = 1, 2 do
- local x, y, w, h = self:get_scroll_button_rect(i)
- if px >= x and px < x + w and py >= y and py < y + h then
- return i
- end
- end
-end
-
-
-function Node:tab_hovered_update(px, py)
- local tab_index = self:get_tab_overlapping_point(px, py)
- self.hovered_tab = tab_index
- self.hovered_close = 0
- self.hovered_scroll_button = 0
- if tab_index then
- local x, y, w, h = self:get_tab_rect(tab_index)
- local cx, cw = close_button_location(x, w)
- if px >= cx and px < cx + cw and py >= y and py < y + h and config.tab_close_button then
- self.hovered_close = tab_index
- end
- else
- self.hovered_scroll_button = self:get_scroll_button_index(px, py) or 0
- end
-end
-
-
-function Node:get_child_overlapping_point(x, y)
- local child
- if self.type == "leaf" then
- return self
- elseif self.type == "hsplit" then
- child = (x < self.b.position.x) and self.a or self.b
- elseif self.type == "vsplit" then
- child = (y < self.b.position.y) and self.a or self.b
- end
- return child:get_child_overlapping_point(x, y)
-end
-
-
-function Node:get_scroll_button_rect(index)
- local w, pad = get_scroll_button_width()
- local h = style.font:get_height() + style.padding.y * 2
- local x = self.position.x + (index == 1 and 0 or self.size.x - w)
- return x, self.position.y, w, h, pad
-end
-
-
-function Node:get_tab_rect(idx)
- local sbw = get_scroll_button_width()
- local maxw = self.size.x - 2 * sbw
- local x0 = self.position.x + sbw
- local x1 = x0 + common.clamp(self.tab_width * (idx - 1) - self.tab_shift, 0, maxw)
- local x2 = x0 + common.clamp(self.tab_width * idx - self.tab_shift, 0, maxw)
- local h = style.font:get_height() + style.padding.y * 2
- return x1, self.position.y, x2 - x1, h
-end
-
-
-function Node:get_divider_rect()
- local x, y = self.position.x, self.position.y
- if self.type == "hsplit" then
- return x + self.a.size.x, y, style.divider_size, self.size.y
- elseif self.type == "vsplit" then
- return x, y + self.a.size.y, self.size.x, style.divider_size
- end
-end
-
-
--- Return two values for x and y axis and each of them is either falsy or a number.
--- A falsy value indicate no fixed size along the corresponding direction.
-function Node:get_locked_size()
- if self.type == "leaf" then
- if self.locked then
- local size = self.active_view.size
- -- The values below should be either a falsy value or a number
- local sx = (self.locked and self.locked.x) and size.x
- local sy = (self.locked and self.locked.y) and size.y
- return sx, sy
- end
- else
- local x1, y1 = self.a:get_locked_size()
- local x2, y2 = self.b:get_locked_size()
- -- The values below should be either a falsy value or a number
- local sx, sy
- if self.type == 'hsplit' then
- if x1 and x2 then
- local dsx = (x1 < 1 or x2 < 1) and 0 or style.divider_size
- sx = x1 + x2 + dsx
- end
- sy = y1 or y2
- else
- if y1 and y2 then
- local dsy = (y1 < 1 or y2 < 1) and 0 or style.divider_size
- sy = y1 + y2 + dsy
- end
- sx = x1 or x2
- end
- return sx, sy
- end
-end
-
-
-local function copy_position_and_size(dst, src)
- dst.position.x, dst.position.y = src.position.x, src.position.y
- dst.size.x, dst.size.y = src.size.x, src.size.y
-end
-
-
--- calculating the sizes is the same for hsplits and vsplits, except the x/y
--- axis are swapped; this function lets us use the same code for both
-local function calc_split_sizes(self, x, y, x1, x2, y1, y2)
- local ds = ((x1 and x1 < 1) or (x2 and x2 < 1)) and 0 or style.divider_size
- local n = x1 and x1 + ds or (x2 and self.size[x] - x2 or math.floor(self.size[x] * self.divider))
- self.a.position[x] = self.position[x]
- self.a.position[y] = self.position[y]
- self.a.size[x] = n - ds
- self.a.size[y] = self.size[y]
- self.b.position[x] = self.position[x] + n
- self.b.position[y] = self.position[y]
- self.b.size[x] = self.size[x] - n
- self.b.size[y] = self.size[y]
-end
-
-
-function Node:update_layout()
- if self.type == "leaf" then
- local av = self.active_view
- if self:should_show_tabs() then
- local _, _, _, th = self:get_tab_rect(1)
- av.position.x, av.position.y = self.position.x, self.position.y + th
- av.size.x, av.size.y = self.size.x, self.size.y - th
- else
- copy_position_and_size(av, self)
- end
- else
- local x1, y1 = self.a:get_locked_size()
- local x2, y2 = self.b:get_locked_size()
- if self.type == "hsplit" then
- calc_split_sizes(self, "x", "y", x1, x2)
- elseif self.type == "vsplit" then
- calc_split_sizes(self, "y", "x", y1, y2)
- end
- self.a:update_layout()
- self.b:update_layout()
- end
-end
-
-
-function Node:scroll_tabs_to_visible()
- local index = self:get_view_idx(self.active_view)
- if index then
- local tabs_number = self:get_visible_tabs_number()
- if self.tab_offset > index then
- self.tab_offset = index
- elseif self.tab_offset + tabs_number - 1 < index then
- self.tab_offset = index - tabs_number + 1
- elseif tabs_number < config.max_tabs and self.tab_offset > 1 then
- self.tab_offset = #self.views - config.max_tabs + 1
- end
- end
-end
-
-
-function Node:scroll_tabs(dir)
- local view_index = self:get_view_idx(self.active_view)
- if dir == 1 then
- if self.tab_offset > 1 then
- self.tab_offset = self.tab_offset - 1
- local last_index = self.tab_offset + self:get_visible_tabs_number() - 1
- if view_index > last_index then
- self:set_active_view(self.views[last_index])
- end
- end
- elseif dir == 2 then
- local tabs_number = self:get_visible_tabs_number()
- if self.tab_offset + tabs_number - 1 < #self.views then
- self.tab_offset = self.tab_offset + 1
- local view_index = self:get_view_idx(self.active_view)
- if view_index < self.tab_offset then
- self:set_active_view(self.views[self.tab_offset])
- end
- end
- end
-end
-
-
-function Node:target_tab_width()
- local n = self:get_visible_tabs_number()
- local w = self.size.x - get_scroll_button_width() * 2
- return common.clamp(style.tab_width, w / config.max_tabs, w / n)
-end
-
-
-function Node:update()
- if self.type == "leaf" then
- self:scroll_tabs_to_visible()
- for _, view in ipairs(self.views) do
- view:update()
- end
- self:tab_hovered_update(self.hovered.x, self.hovered.y)
- local tab_width = self:target_tab_width()
- self:move_towards("tab_shift", tab_width * (self.tab_offset - 1))
- self:move_towards("tab_width", tab_width)
- else
- self.a:update()
- self.b:update()
- end
-end
-
-function Node:draw_tab(text, is_active, is_hovered, is_close_hovered, x, y, w, h, standalone)
- local ds = style.divider_size
- local dots_width = style.font:get_width("…")
- local color = style.dim
- local padding_y = style.padding.y
- renderer.draw_rect(x + w, y + padding_y, ds, h - padding_y * 2, style.dim)
- if standalone then
- renderer.draw_rect(x-1, y-1, w+2, h+2, style.background2)
- end
- if is_active then
- color = style.text
- renderer.draw_rect(x, y, w, h, style.background)
- renderer.draw_rect(x + w, y, ds, h, style.divider)
- renderer.draw_rect(x - ds, y, ds, h, style.divider)
- end
- local cx, cw, cspace = close_button_location(x, w)
- local show_close_button = ((is_active or is_hovered) and not standalone and config.tab_close_button)
- if show_close_button then
- local close_style = is_close_hovered and style.text or style.dim
- common.draw_text(style.icon_font, close_style, "C", nil, cx, y, 0, h)
- end
- if is_hovered then
- color = style.text
- end
- local padx = style.padding.x
- -- Normally we should substract "cspace" from text_avail_width and from the
- -- clipping width. It is the padding space we give to the left and right of the
- -- close button. However, since we are using dots to terminate filenames, we
- -- choose to ignore "cspace" accepting that the text can possibly "touch" the
- -- close button.
- local text_avail_width = cx - x - padx
- core.push_clip_rect(x, y, cx - x, h)
- x, w = x + padx, w - padx * 2
- local align = "center"
- if style.font:get_width(text) > text_avail_width then
- align = "left"
- for i = 1, #text do
- local reduced_text = text:sub(1, #text - i)
- if style.font:get_width(reduced_text) + dots_width <= text_avail_width then
- text = reduced_text .. "…"
- break
- end
- end
- end
- common.draw_text(style.font, color, text, align, x, y, w, h)
- core.pop_clip_rect()
-end
-
-function Node:draw_tabs()
- local x, y, w, h, scroll_padding = self:get_scroll_button_rect(1)
- local ds = style.divider_size
- local dots_width = style.font:get_width("…")
- core.push_clip_rect(x, y, self.size.x, h)
- renderer.draw_rect(x, y, self.size.x, h, style.background2)
- renderer.draw_rect(x, y + h - ds, self.size.x, ds, style.divider)
-
- if self.tab_offset > 1 then
- local button_style = self.hovered_scroll_button == 1 and style.text or style.dim
- common.draw_text(style.icon_font, button_style, "<", nil, x + scroll_padding, y, 0, h)
- end
-
- local tabs_number = self:get_visible_tabs_number()
- if #self.views > self.tab_offset + tabs_number - 1 then
- local xrb, yrb, wrb = self:get_scroll_button_rect(2)
- local button_style = self.hovered_scroll_button == 2 and style.text or style.dim
- common.draw_text(style.icon_font, button_style, ">", nil, xrb + scroll_padding, yrb, 0, h)
- end
-
- for i = self.tab_offset, self.tab_offset + tabs_number - 1 do
- local view = self.views[i]
- local x, y, w, h = self:get_tab_rect(i)
- self:draw_tab(view:get_name(), view == self.active_view,
- i == self.hovered_tab, i == self.hovered_close,
- x, y, w, h)
- end
-
- core.pop_clip_rect()
-end
-
-
-function Node:draw()
- if self.type == "leaf" then
- if self:should_show_tabs() then
- self:draw_tabs()
- end
- local pos, size = self.active_view.position, self.active_view.size
- core.push_clip_rect(pos.x, pos.y, size.x, size.y)
- self.active_view:draw()
- core.pop_clip_rect()
- else
- local x, y, w, h = self:get_divider_rect()
- renderer.draw_rect(x, y, w, h, style.divider)
- self:propagate("draw")
- end
-end
-
-
-function Node:is_empty()
- if self.type == "leaf" then
- return #self.views == 0 or (#self.views == 1 and self.views[1]:is(EmptyView))
- else
- return self.a:is_empty() and self.b:is_empty()
- end
-end
-
-
-function Node:close_all_docviews(keep_active)
- local node_active_view = self.active_view
- local lost_active_view = false
- if self.type == "leaf" then
- local i = 1
- while i <= #self.views do
- local view = self.views[i]
- if view.context == "session" and (not keep_active or view ~= self.active_view) then
- table.remove(self.views, i)
- if view == node_active_view then
- lost_active_view = true
- end
- else
- i = i + 1
- end
- end
- self.tab_offset = 1
- if #self.views == 0 and self.is_primary_node then
- -- if we are not the primary view and we had the active view it doesn't
- -- matter to reattribute the active view because, within the close_all_docviews
- -- top call, the primary node will take the active view anyway.
- -- Set the empty view and takes the active view.
- self:add_view(EmptyView())
- elseif #self.views > 0 and lost_active_view then
- -- In practice we never get there but if a view remain we need
- -- to reset the Node's active view.
- self:set_active_view(self.views[1])
- end
- else
- self.a:close_all_docviews(keep_active)
- self.b:close_all_docviews(keep_active)
- if self.a:is_empty() and not self.a.is_primary_node then
- self:consume(self.b)
- elseif self.b:is_empty() and not self.b.is_primary_node then
- self:consume(self.a)
- end
- end
-end
-
--- Returns true for nodes that accept either "proportional" resizes (based on the
--- node.divider) or "locked" resizable nodes (along the resize axis).
-function Node:is_resizable(axis)
- if self.type == 'leaf' then
- return not self.locked or not self.locked[axis] or self.resizable
- else
- local a_resizable = self.a:is_resizable(axis)
- local b_resizable = self.b:is_resizable(axis)
- return a_resizable and b_resizable
- end
-end
-
-
--- Return true iff it is a locked pane along the rezise axis and is
--- declared "resizable".
-function Node:is_locked_resizable(axis)
- return self.locked and self.locked[axis] and self.resizable
-end
-
-
-function Node:resize(axis, value)
- -- the application works fine with non-integer values but to have pixel-perfect
- -- placements of view elements, like the scrollbar, we round the value to be
- -- an integer.
- value = math.floor(value)
- if self.type == 'leaf' then
- -- If it is not locked we don't accept the
- -- resize operation here because for proportional panes the resize is
- -- done using the "divider" value of the parent node.
- if self:is_locked_resizable(axis) then
- return self.active_view:set_target_size(axis, value)
- end
- else
- if self.type == (axis == "x" and "hsplit" or "vsplit") then
- -- we are resizing a node that is splitted along the resize axis
- if self.a:is_locked_resizable(axis) and self.b:is_locked_resizable(axis) then
- local rem_value = value - self.a.size[axis]
- if rem_value >= 0 then
- return self.b.active_view:set_target_size(axis, rem_value)
- else
- self.b.active_view:set_target_size(axis, 0)
- return self.a.active_view:set_target_size(axis, value)
- end
- end
- else
- -- we are resizing a node that is splitted along the axis perpendicular
- -- to the resize axis
- local a_resizable = self.a:is_resizable(axis)
- local b_resizable = self.b:is_resizable(axis)
- if a_resizable and b_resizable then
- self.a:resize(axis, value)
- self.b:resize(axis, value)
- end
- end
- end
-end
-
-
-function Node:get_split_type(mouse_x, mouse_y)
- local x, y = self.position.x, self.position.y
- local w, h = self.size.x, self.size.y
- local _, _, _, tab_h = self:get_scroll_button_rect(1)
- y = y + tab_h
- h = h - tab_h
-
- local local_mouse_x = mouse_x - x
- local local_mouse_y = mouse_y - y
-
- if local_mouse_y < 0 then
- return "tab"
- else
- local left_pct = local_mouse_x * 100 / w
- local top_pct = local_mouse_y * 100 / h
- if left_pct <= 30 then
- return "left"
- elseif left_pct >= 70 then
- return "right"
- elseif top_pct <= 30 then
- return "up"
- elseif top_pct >= 70 then
- return "down"
- end
- return "middle"
- end
-end
-
-
-function Node:get_drag_overlay_tab_position(x, y, dragged_node, dragged_index)
- local tab_index = self:get_tab_overlapping_point(x, y)
- if not tab_index then
- local first_tab_x = self:get_tab_rect(1)
- if x < first_tab_x then
- -- mouse before first visible tab
- tab_index = self.tab_offset or 1
- else
- -- mouse after last visible tab
- tab_index = self:get_visible_tabs_number() + (self.tab_offset - 1 or 0)
- end
- end
- local tab_x, tab_y, tab_w, tab_h = self:get_tab_rect(tab_index)
- if x > tab_x + tab_w / 2 and tab_index <= #self.views then
- -- use next tab
- tab_x = tab_x + tab_w
- tab_index = tab_index + 1
- end
- if self == dragged_node and dragged_index and tab_index > dragged_index then
- -- the tab we are moving is counted in tab_index
- tab_index = tab_index - 1
- tab_x = tab_x - tab_w
- end
- return tab_index, tab_x, tab_y, tab_w, tab_h
-end
-
-
local RootView = View:extend()
function RootView:new()
@@ -1068,7 +299,7 @@ end
function RootView:update()
- copy_position_and_size(self.root_node, self)
+ Node.copy_position_and_size(self.root_node, self)
self.root_node:update()
self.root_node:update_layout()