From b37c190db22489c7978012550cbb5f884d5e62bf Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Sun, 16 May 2021 15:50:27 +0200 Subject: [PATCH] Add buttons to scroll tabs when there are too many Use config.max_tabs to configure the maximum number of tabs that can be shown. Beyond the maximum value scroll buttons will appear to scroll the tabs. --- data/core/config.lua | 1 + data/core/rootview.lua | 136 ++++++++++++++++++++++++--- data/fonts/icons.ttf | Bin 9620 -> 10036 bytes dev-utils/fontello-config-small.json | 118 ----------------------- 4 files changed, 122 insertions(+), 133 deletions(-) delete mode 100644 dev-utils/fontello-config-small.json diff --git a/data/core/config.lua b/data/core/config.lua index f9b4e87f..74ca3d2f 100644 --- a/data/core/config.lua +++ b/data/core/config.lua @@ -11,6 +11,7 @@ config.symbol_pattern = "[%a_][%w_]*" config.non_word_chars = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-" config.undo_merge_timeout = 0.3 config.max_undos = 10000 +config.max_tabs = 10 config.highlight_current_line = true config.line_height = 1.2 config.indent_size = 2 diff --git a/data/core/rootview.lua b/data/core/rootview.lua index da628c7e..1ed571a4 100644 --- a/data/core/rootview.lua +++ b/data/core/rootview.lua @@ -1,5 +1,6 @@ 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" @@ -59,7 +60,9 @@ function Node:new(type) end self.hovered = {x = -1, y = -1 } self.hovered_close = 0 - self.tab_margin = style.padding.x + self.tab_shift = 0 + self.tab_offset = 1 + self.tab_width = style.tab_width self.move_towards = View.move_towards end @@ -236,11 +239,20 @@ function Node:get_divider_overlapping_point(px, py) 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 #self.views == 1 then return nil end - local x, y, w, h = self:get_tab_rect(1) - if px >= x and py >= y and px < x + w * #self.views and py < y + h then - return math.floor((px - x) / w) + 1 + local tabs_number = self:get_visible_tabs_number() + -- FIXME: looping over tabs is may be not needed. Find a simpler way. + for i = self.tab_offset, self.tab_offset + tabs_number - 1 do + local x, y, w, h = self:get_tab_rect(i) + if px >= x and py >= y and px < x + w and py < y + h then + return i + end end end @@ -252,18 +264,33 @@ local function close_button_location(x, w) end +function Node:get_scroll_button_index(px, py) + 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_tab_scroll = 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 then self.hovered_close = tab_index - return + end + else + local tab_scroll_index = self:get_scroll_button_index(px, py) + if tab_scroll_index then + self.hovered_tab_scroll = tab_scroll_index end end - self.hovered_close = 0 end @@ -280,10 +307,24 @@ function Node:get_child_overlapping_point(x, y) end +function Node:get_scroll_button_rect(index) + local w = style.icon_font:get_width(">") + local pad = w + local h = style.font:get_height() + style.padding.y * 2 + if index == 1 then + return self.position.x, self.position.y, w + 2 * pad, h, pad + else + return self.position.x + self.size.x - w - 2 * pad, self.position.y, w + 2 * pad, h, pad + end +end + + function Node:get_tab_rect(idx) - local tw = math.min(style.tab_width, (self.size.x - self.tab_margin) / #self.views) - local x_left = self.position.x + tw * (idx - 1) - local x1, x2 = math.floor(x_left), math.floor(x_left + tw) + -- FIXME: we may consider to create a separate method just to get the + -- width of the scroll button. It is needed to compute tabs placement. + local _, _, sbw = self:get_scroll_button_rect(1) + local x_left = self.position.x + sbw + self.tab_width * (idx - 1) - self.tab_shift + local x1, x2 = math.floor(x_left), math.floor(x_left + self.tab_width) local h = style.font:get_height() + style.padding.y * 2 return x1, self.position.y, x2 - x1, h end @@ -386,13 +427,61 @@ function Node:update_layout() 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 tabs_number = self:get_visible_tabs_number() + local _, _, sbw = self:get_scroll_button_rect(1) + return math.min(style.tab_width, (self.size.x - sbw * 2) / tabs_number) +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) - self:move_towards("tab_margin", style.padding.x) + 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() @@ -401,14 +490,27 @@ end function Node:draw_tabs() - local x, y, _, h = self:get_tab_rect(1) + 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) - for i, view in ipairs(self.views) do + if self.tab_offset > 1 then + local button_style = self.hovered_tab_scroll == 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_tab_scroll == 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) local text = view:get_name() local color = style.dim @@ -637,11 +739,13 @@ function RootView:on_mouse_pressed(button, x, y, clicks) return end local node = self.root_node:get_child_overlapping_point(x, y) + if node.hovered_tab_scroll > 0 then + node:scroll_tabs(node.hovered_tab_scroll) + return + end local idx = node:get_tab_overlapping_point(x, y) if idx then if button == "middle" or node.hovered_close == idx then - local _, _, tw = node:get_tab_rect(idx) - node.tab_margin = node.tab_margin + tw node:close_view(self.root_node, node.views[idx]) else self.dragged_node = idx @@ -702,7 +806,9 @@ function RootView:on_mouse_moved(x, y, dx, dy) local node = self.root_node:get_child_overlapping_point(x, y) local div = self.root_node:get_divider_overlapping_point(x, y) local tab_index = node and node:get_tab_overlapping_point(x, y) - if div then + if node and node:get_scroll_button_index(x, y) then + system.set_cursor("arrow") + elseif div then local axis = (div.type == "hsplit" and "x" or "y") if div.a:is_resizable(axis) and div.b:is_resizable(axis) then system.set_cursor(div.type == "hsplit" and "sizeh" or "sizev") diff --git a/data/fonts/icons.ttf b/data/fonts/icons.ttf index 00b4cc3bd262b1cb9f3339d2e5379c4b45f983c2..43ab376735b7a7a4355a91dacd639d4dcc8d285e 100644 GIT binary patch delta 1138 zcmb`G-%C?r7{{OYoU_fHGq<^&ZIaW?A8qp&O$ogjCgz2uiIn++8nWqr7@Hn+s8P5m zx(iW`7hZ(cMN&d2Orh>V=*H_n3F)eESQkMEMz!_4XGiD{XfNOQ`##V6>^YzJJ==2k zcJE?UI`jvC^Z~%YXfh=EUv&F0ehI)FkItmr23NirptK+N8=_Jy8E*P@590>_Mnfz# zEn(h_aSwV?EO9rg+q32XxMke`7LSC+%FC9509LGrmN+JKyX;qt-(Xx5Pp0O&20!G# zVB!!H{fWuZ(D_7d7(n!6+>i{-Nu=EP7USC(yC*`)$c}OP2gdgi@Rc+_0R|bXp^(V z#ZuQYO|qZiTtum%b#&B#M;Sh#qYmoi>x@@t1rd8{Wx`g=YfM>y0}b}n>WXltA3Z)& z05IUP*r;T{M9mB(7O)l)>MUZDd3li$VgY^oRLBQ6R8dg8SVhI?Lj_$03O}NXo?uKB{tO_hssufz%7Q+w zik@>;m6(Zgn&O6AwAt&#eGBV0xr!I(*(U`-CK@-Qzc5OvXBDu z7^`TiT@ToRMl-|1!`luAqbse~Gj`ELv{hc>x!6itnq9;$G7GtVQp@wfwBQtSVL>3P zd<7rmnT^~*ZeLmQWDT9H3PO;tz>WpblnScEe`)DmBzfEH$~+Mj~cX*SG`m;xtYVzbB&hws-vow@9@v^s1w z&t~%{B!d5>hp0U&dBSqVTSw-Z0p=NV%673EoW%Xs_%$n)!+mDQY0V^xaxc~qF delta 718 zcmX|8OK4L;6g~64wMp~ZJZ;1T6VsRqwxAX2!a${U;U`Mb(h4r>OPY_+*hdqz;-b)% zxU!BQO2r^uR8X@>UAS>!K?G4;NH@B0BSz^$)M7v|-bn@-&O7g%xpVKGxvQscowyO5 zPjv!p6u@}8;JCYQC$^AY0hH(IOQnc6+!z45S82a5>*fln{eP~K{sj2EIj887Pv^TI z5x3>0uVgDPhduyW9_U!gXPl|f+UrArU!(oOJQ?N-^(*NF=~%u{nj4uo-1tn!c`}mI zGifJ09<7rW3HKD7IhO@}&q%)~9l7WfGEaTQpQL|LgAd)AV(Gxn)8Bw?Woo+Joz1w} z;mQ)_-dL=kW7qi@%k{5=(P7{n)aG9waXW>tL&GxpA-aJIFLq%s`uPS6b4%G5WaPJ2 z1kvV-L9969Jf-wfDuZ{aJ-n&}`F+J0)O4d+1u)wHdP6D!kGJJ@2=c+|L|4m@Uq7W- z;AO?MT7k9y{sXmlewdVic)Sq_o%o1^D43KmiBC#UeTabyP$M`aAv&6n@DQJs5Va>I zM5j|N3{b~FR>C6ANzlY`K|);UvV^~SQ>(D*qWRksx;u1lWztG)!cv>J`gmvU4TV84 zU$zfgw(!+jW0PfQmJ9rceXQpRt1F{QSyj|m+L(4t`>yZMkLqRpuQ6-9Z4S?_rnY`j Vn9Yv|;=B}y@yCI3HMza!`v*8uh9>|3 diff --git a/dev-utils/fontello-config-small.json b/dev-utils/fontello-config-small.json deleted file mode 100644 index 895de303..00000000 --- a/dev-utils/fontello-config-small.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "name": "icons", - "css_prefix_text": "icon-", - "css_use_suffix": false, - "hinting": true, - "units_per_em": 1000, - "ascent": 850, - "glyphs": [ - { - "uid": "9dd9e835aebe1060ba7190ad2b2ed951", - "css": "search-1", - "code": 76, - "src": "fontawesome" - }, - { - "uid": "c76b7947c957c9b78b11741173c8349b", - "css": "attention-1", - "code": 33, - "src": "fontawesome" - }, - { - "uid": "1b5a5d7b7e3c71437f5a26befdd045ed", - "css": "doc-1", - "code": 102, - "src": "fontawesome" - }, - { - "uid": "f8aa663c489bcbd6e68ec8147dca841e", - "css": "folder-1", - "code": 100, - "src": "fontawesome" - }, - { - "uid": "c95735c17a10af81448c7fed98a04546", - "css": "folder-open-1", - "code": 68, - "src": "fontawesome" - }, - { - "uid": "e99461abfef3923546da8d745372c995", - "css": "cog", - "code": 80, - "src": "fontawesome" - }, - { - "uid": "7bf14281af5633a597f85b061ef1cfb9", - "css": "angle-right", - "code": 43, - "src": "fontawesome" - }, - { - "uid": "e4dde1992f787163e2e2b534b8c8067d", - "css": "angle-down", - "code": 45, - "src": "fontawesome" - }, - { - "uid": "ea2d9a8c51ca42b38ef0d2a07f16d9a7", - "css": "chart-line", - "code": 103, - "src": "fontawesome" - }, - { - "uid": "f4445feb55521283572ee88bc304f928", - "css": "floppy", - "code": 83, - "src": "fontawesome" - }, - { - "uid": "9755f76110ae4d12ac5f9466c9152031", - "css": "book", - "code": 66, - "src": "fontawesome" - }, - { - "uid": "e82cedfa1d5f15b00c5a81c9bd731ea2", - "css": "info-circled-1", - "code": 105, - "src": "fontawesome" - }, - { - "uid": "5211af474d3a9848f67f945e2ccaf143", - "css": "cancel-1", - "code": 67, - "src": "fontawesome" - }, - { - "uid": "04f022b8bd044d4ccfffd3887ff72088", - "css": "window-minimize", - "code": 95, - "src": "fontawesome" - }, - { - "uid": "d0e62145dbf40f30e47b3819b8b43a8f", - "css": "window-restore", - "code": 119, - "src": "fontawesome" - }, - { - "uid": "7394501fc0b17cb7bda99538f92e26d6", - "css": "window-close", - "code": 88, - "src": "fontawesome" - }, - { - "uid": "559647a6f430b3aeadbecd67194451dd", - "css": "menu-1", - "code": 77, - "src": "fontawesome" - }, - { - "uid": "07f0832c07f3d9713fffb06c8bffa027", - "css": "window-maximize", - "code": 87, - "src": "fontawesome" - } - ] -} \ No newline at end of file