From f0a9d5818fb96e3a719c1ac4988af1e7773a427f Mon Sep 17 00:00:00 2001 From: Insality Date: Wed, 16 Sep 2020 00:34:00 +0300 Subject: [PATCH 01/30] Add infinity list component --- druid/base/grid.lua | 143 ++++++++++++++++++++++++++------ druid/base/infinity_list.lua | 117 ++++++++++++++++++++++++++ druid/const.lua | 6 ++ druid/system/druid_instance.lua | 13 ++- 4 files changed, 253 insertions(+), 26 deletions(-) create mode 100644 druid/base/infinity_list.lua diff --git a/druid/base/grid.lua b/druid/base/grid.lua index c509f9d..fd4fe48 100644 --- a/druid/base/grid.lua +++ b/druid/base/grid.lua @@ -37,6 +37,7 @@ function M.init(self, parent, element, in_row) self.nodes = {} self.offset = vmath.vector3(0) + self.grid_mode = const.GRID_MODE.DYNAMIC local pivot = helper.get_pivot_offset(gui.get_pivot(self.parent)) self.anchor = vmath.vector3(0.5 + pivot.x, 0.5 - pivot.y, 0) @@ -50,22 +51,29 @@ function M.init(self, parent, element, in_row) self.on_remove_item = Event() self.on_clear = Event() self.on_update_positions = Event() + + self._set_position_function = gui.set_position end -local function check_border(self, pos) - local border = self.border +local function _update_border(self, pos, border) local size = self.node_size - local W = pos.x - size.x/2 + self.border_offset.x - local E = pos.x + size.x/2 + self.border_offset.x - local N = pos.y + size.y/2 + self.border_offset.y - local S = pos.y - size.y/2 + self.border_offset.y + local left = pos.x - size.x/2 + self.border_offset.x + local right = pos.x + size.x/2 + self.border_offset.x + local top = pos.y + size.y/2 + self.border_offset.y + local bottom = pos.y - size.y/2 + self.border_offset.y - border.x = math.min(border.x, W) - border.y = math.max(border.y, N) - border.z = math.max(border.z, E) - border.w = math.min(border.w, S) + border.x = math.min(border.x, left) + border.y = math.max(border.y, top) + border.z = math.max(border.z, right) + border.w = math.min(border.w, bottom) +end + + +local function update_border_offset(self, pos) + local border = self.border + _update_border(self, pos, border) self.border_offset = vmath.vector3( (border.x + (border.z - border.x) * self.anchor.x), @@ -75,8 +83,25 @@ local function check_border(self, pos) end +local function update_pos(self, is_instant) + for i, node in pairs(self.nodes) do + if is_instant then + gui.set_position(node, self:get_pos(i)) + else + self._set_position_function(node, self:get_pos(i)) + end + end + + self.on_update_positions:trigger(self:get_context()) +end + + local temp_pos = vmath.vector3(0) -local function get_pos(self, index) +--- Return pos for grid node index +-- @function grid:get_pos +-- @tparam number index The grid element index +-- @treturn vector3 Node position +function M.get_pos(self, index) local row = math.ceil(index / self.in_row) - 1 local col = (index - row * self.in_row) - 1 @@ -88,13 +113,16 @@ local function get_pos(self, index) end -local function update_pos(self, is_instant) - for i = 1, #self.nodes do - local node = self.nodes[i] - gui.set_position(node, get_pos(self, i)) - end +--- Return index for grid pos +-- @function grid:get_index +-- @tparam vector3 pos The node position in the grid +-- @treturn number The node index +function M.get_index(self, pos) + local col = (pos.x + self.border_offset.x) / (self.node_size.x + self.offset.x) + local row = -(pos.y + self.border_offset.y) / (self.node_size.y + self.offset.y) - self.on_update_positions:trigger(self:get_context()) + local index = col + (row * self.in_row) + 1 + return math.floor(index) end @@ -127,41 +155,99 @@ end -- @tparam[opt] number index The item position. By default add as last item function M.add(self, item, index) index = index or (#self.nodes + 1) - table.insert(self.nodes, index, item) + + if self.grid_mode == const.GRID_MODE.DYNAMIC then + table.insert(self.nodes, index, item) + else + self.nodes[index] = item + end + gui.set_parent(item, self.parent) - local pos = get_pos(self, index) - check_border(self, pos) + local pos = self:get_pos(index) + for i, _ in pairs(self.nodes) do + update_border_offset(self, self:get_pos(i)) + end + + -- Add new item instantly in new pos + gui.set_position(item, pos) update_pos(self) self.on_add_item:trigger(self:get_context(), item, index) end +function M:remove(index, delete_node) + assert(self.nodes[index], "No grid item at given index " .. index) + + local parent_node = self.nodes[index] + if delete_node then + gui.delete_node(parent_node) + end + + if self.grid_mode == const.GRID_MODE.DYNAMIC then + table.remove(self.nodes, index) + else + self.nodes[index] = nil + end + + -- Recalculate borders + self.border = vmath.vector4(0) + update_border_offset(self, self:get_pos(1)) + for i, _ in pairs(self.nodes) do + local pos = self:get_pos(i) + update_border_offset(self, pos) + end + + update_pos(self) +end + + --- Return grid content size -- @function grid:get_size -- @treturn vector3 The grid content size -function M.get_size(self) +function M.get_size(self, border) + border = border or self.border return vmath.vector3( - self.border.z - self.border.x, - self.border.y - self.border.w, + border.z - border.x, + border.y - border.w, 0) end +function M:get_size_for_elements_count(count) + local border = vmath.vector4(0) + for i = 1, count do + local pos = self:get_pos(i) + _update_border(self, pos, border) + end + + return M.get_size(self, border) +end + + --- Return array of all node positions -- @function grid:get_all_pos -- @treturn vector3[] All grid node positions function M.get_all_pos(self) local result = {} - for i = 1, #self.nodes do - table.insert(result, gui.get_position(self.nodes[i])) + for i, node in pairs(self.nodes) do + table.insert(result, gui.get_position(node)) end return result end +--- Change set position function for grid nodes. It will call on +-- update poses on grid elements. Default: gui.set_position +-- @function grid:set_position_function +-- @tparam function callback Function on node set position +function M.set_position_function(self, callback) + self._set_position_function = callback or gui.set_position +end + + --- Clear grid nodes array. GUI nodes will be not deleted! -- If you want to delete GUI nodes, use grid.nodes array before grid:clear -- @function grid:clear @@ -175,4 +261,11 @@ function M.clear(self) end +function M:set_grid_mode(grid_mode) + assert(grid_mode == const.GRID_MODE.STATIC or grid_mode == const.GRID_MODE.DYNAMIC) + + self.grid_mode = grid_mode +end + + return M diff --git a/druid/base/infinity_list.lua b/druid/base/infinity_list.lua new file mode 100644 index 0000000..b44f522 --- /dev/null +++ b/druid/base/infinity_list.lua @@ -0,0 +1,117 @@ +--- Manage data for huge dataset in scroll +--- It requires basic druid scroll and druid grid components +local const = require("druid.const") +local component = require("druid.component") + +local M = component.create("infinity_list", { const.ON_UPDATE }) + + +function M:init(data_list, scroll, grid, create_function) + self.view_size = gui.get_size(scroll.view_node) + self.prefab_size = grid.node_size + self.druid = self:get_druid() + self.scroll = scroll + self.grid = grid + self.grid:set_grid_mode(const.GRID_MODE.STATIC) + + self.data = data_list + self.top_index = 1 + + self.create_function = create_function + + self.nodes = {} + self.components = {} + + self.elements_view_count = vmath.vector3( + math.ceil(self.view_size.x / self.prefab_size.x), + math.ceil(self.view_size.y / self.prefab_size.y), + 0) + + self:_refresh() + self.scroll.on_scroll:subscribe(function() self._check_elements(self) end) +end + + +function M:on_remove() + -- TODO: make this work + -- self.scroll.on_scroll:unsubscribe(self._check_elements) +end + + +function M:update(dt) + if self.scroll.animate then + self:_check_elements() + end +end + + +function M:set_data(data_list) + self.data = data_list + self:_refresh() +end + + +function M:_add_at(index) + if self.nodes[index] then + self:_remove_at(index) + end + + local node, instance = self.create_function(self.data[index], index) + self.grid:add(node, index) + self.nodes[index] = node + self.components[index] = instance +end + + +function M:_remove_at(index) + self.grid:remove(index) + + local node = self.nodes[index] + gui.delete_node(node) + self.nodes[index] = nil + + if self.components[index] then + self.druid:remove(self.components[index]) + self.components[index] = nil + end +end + + +function M:_refresh() + for index, _ in pairs(self.nodes) do + self:_remove_at(index) + end + self:_check_elements() + self:_recalc_scroll_size() +end + + +function M:_check_elements() + local pos = gui.get_position(self.scroll.content_node) + pos.y = -pos.y + local top_index = self.grid:get_index(pos) + local last_index = top_index + (self.elements_view_count.x * self.elements_view_count.y) + 1 + + -- Clear outside elements + for index, _ in pairs(self.nodes) do + if index < top_index or index > last_index then + self:_remove_at(index) + end + end + + -- Spawn current elements + for index = top_index, last_index do + if self.data[index] and not self.nodes[index] then + self:_add_at(index) + end + end +end + + +function M:_recalc_scroll_size() + local element_size = self.grid:get_size_for_elements_count(#self.data) + self.scroll:set_size(element_size) +end + + +return M diff --git a/druid/const.lua b/druid/const.lua index cad9f8a..858d1c7 100644 --- a/druid/const.lua +++ b/druid/const.lua @@ -102,6 +102,12 @@ M.SWIPE = { } +M.GRID_MODE = { + STATIC = "static", + DYNAMIC = "dynamic", +} + + M.EMPTY_FUNCTION = function() end M.EMPTY_STRING = "" M.SPACE_STRING = " " diff --git a/druid/system/druid_instance.lua b/druid/system/druid_instance.lua index 0b1191c..2a9da6c 100644 --- a/druid/system/druid_instance.lua +++ b/druid/system/druid_instance.lua @@ -17,6 +17,7 @@ -- @see druid.radio_group -- @see druid.swipe -- @see druid.drag +-- @see druid.infinity_list local const = require("druid.const") local druid_input = require("druid.helper.druid_input") @@ -40,6 +41,7 @@ local radio_group = require("druid.base.radio_group") local input = require("druid.base.input") local swipe = require("druid.base.swipe") local drag = require("druid.base.drag") +local infinity_list = require("druid.base.infinity_list") -- local infinity_scroll = require("druid.base.infinity_scroll") -- @classmod Druid @@ -477,10 +479,19 @@ end --- Create drag basic component -- @function druid:new_drag -- @tparam args ... drag init args --- @treturn Componetn drag component +-- @treturn Component drag component function Druid.new_drag(self, ...) return Druid.create(self, drag, ...) end +--- Create infinity list basic component +-- @function druid:new_infinity_list +-- @tparam args ... drag init args +-- @treturn Component drag component +function Druid.new_infinity_list(self, ...) + return Druid.create(self, infinity_list, ...) +end + + return Druid From 7a6e8bbef3148b263ea1be4fae9de998e362e322 Mon Sep 17 00:00:00 2001 From: Insality Date: Wed, 16 Sep 2020 00:34:38 +0300 Subject: [PATCH 02/30] Add example of infinity scroll --- example/gui/main/main.gui | 354 ++++++++++++++++++++++++++++++- example/gui/main/main.gui_script | 5 +- example/lang.lua | 2 + example/page/infinity_page.lua | 47 ++++ 4 files changed, 400 insertions(+), 8 deletions(-) create mode 100644 example/page/infinity_page.lua diff --git a/example/gui/main/main.gui b/example/gui/main/main.gui index d5a321f..d5bce3c 100644 --- a/example/gui/main/main.gui +++ b/example/gui/main/main.gui @@ -10453,6 +10453,352 @@ nodes { text_leading: 1.0 text_tracking: 0.0 } +nodes { + position { + x: 4200.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 1.0 + y: 1.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "infinity_page" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_STRETCH + parent: "C_Anchor" + layer: "image" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 0.0 + y: 200.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 300.0 + y: 400.0 + z: 0.0 + w: 1.0 + } + color { + x: 0.8 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "" + id: "infinity_scroll_stencil" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_N + adjust_mode: ADJUST_MODE_FIT + parent: "infinity_page" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_STENCIL + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 400.0 + y: 400.0 + z: 0.0 + w: 1.0 + } + color { + x: 0.8 + y: 1.0 + z: 0.8 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "infinity_scroll_content" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_N + adjust_mode: ADJUST_MODE_FIT + parent: "infinity_scroll_stencil" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 160.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 300.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/button_blue" + id: "infinity_prefab" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "infinity_page" + layer: "" + inherit_alpha: true + slice9 { + x: 20.0 + y: 0.0 + z: 20.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 3.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.75 + y: 0.75 + z: 1.0 + w: 1.0 + } + size { + x: 380.0 + y: 50.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Infinity element 1" + font: "game" + id: "infinity_text" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.3019608 + y: 0.4 + z: 0.8 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "infinity_prefab" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 0.0 + y: 240.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 400.0 + y: 50.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Infinity scroll:" + font: "game" + id: "infinity_header" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "infinity_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 0.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} nodes { position { x: 0.0 @@ -10961,11 +11307,5 @@ layers { name: "text_top" } material: "/builtins/materials/gui.material" -layouts { - name: "Landscape" -} -layouts { - name: "Portrait" -} adjust_reference: ADJUST_REFERENCE_PARENT -max_nodes: 512 +max_nodes: 1024 diff --git a/example/gui/main/main.gui_script b/example/gui/main/main.gui_script index 8973264..44c4c21 100644 --- a/example/gui/main/main.gui_script +++ b/example/gui/main/main.gui_script @@ -10,6 +10,7 @@ local scroll_page = require("example.page.scroll_page") local slider_page = require("example.page.slider_page") local input_page = require("example.page.input_page") local grid_page = require("example.page.grid_page") +local infinity_page = require("example.page.infinity_page") local pages = { "main_page", @@ -19,6 +20,7 @@ local pages = { "slider_page", "input_page", "grid_page", + "infinity_page", } local function on_control_button(self, delta) @@ -65,7 +67,7 @@ function init(self) init_swipe_control(self) - self.page = 1 + self.page = 8 main_page.setup_page(self) text_page.setup_page(self) button_page.setup_page(self) @@ -73,6 +75,7 @@ function init(self) slider_page.setup_page(self) input_page.setup_page(self) grid_page.setup_page(self) + infinity_page.setup_page(self) init_top_panel(self) diff --git a/example/lang.lua b/example/lang.lua index 245f211..b4a68f3 100644 --- a/example/lang.lua +++ b/example/lang.lua @@ -10,6 +10,7 @@ local en = { slider_page = "Slider page", input_page = "Input page", grid_page = "Grid page", + infinity_page = "Infinity scroll", ui_section_button = "Button", ui_section_text = "Text", ui_section_timer = "Timer", @@ -29,6 +30,7 @@ local ru = { slider_page = "Слайдеры", input_page = "Текст. ввод", grid_page = "Сетка", + infinity_page = "Беск. скролл", ui_section_button = "Кнопка", ui_section_text = "Текст", ui_section_timer = "Таймер", diff --git a/example/page/infinity_page.lua b/example/page/infinity_page.lua new file mode 100644 index 0000000..133f26f --- /dev/null +++ b/example/page/infinity_page.lua @@ -0,0 +1,47 @@ +local M = {} + + +local function create_infinity_instance(self, record, index) + local instance = gui.clone_tree(self.infinity_prefab) + gui.set_enabled(instance["infinity_prefab"], true) + gui.set_text(instance["infinity_text"], "Infinity record " .. index) + + local button = self.druid:new_button(instance["infinity_prefab"], function() + print("Infinity click on", index) + end) + + return instance["infinity_prefab"], button +end + + + +local function setup_infinity_list(self) + local data = {} + for i = 1, 2500 do + table.insert(data, i) + end + + self.infinity_list = self.druid:new_infinity_list(data, self.infinity_scroll, self.infinity_grid, function(record, index) + -- function should return gui_node, [druid_component] + return create_infinity_instance(self, record, index) + end) + + -- scroll to some index + local pos = self.infinity_grid:get_pos(950) + self.infinity_scroll:scroll_to(pos, true) +end + + +function M.setup_page(self) + self.infinity_scroll = self.druid:new_scroll("infinity_scroll_stencil", "infinity_scroll_content") + self.infinity_grid = self.druid:new_grid("infinity_scroll_content", "infinity_prefab", 1) + self.infinity_grid:set_offset(vmath.vector3(0, 8, 0)) + + self.infinity_prefab = gui.get_node("infinity_prefab") + gui.set_enabled(self.infinity_prefab, false) + + setup_infinity_list(self) +end + + +return M From f027d4ac96d71bb407c67be985750206faad5065 Mon Sep 17 00:00:00 2001 From: Insality Date: Wed, 16 Sep 2020 00:47:24 +0300 Subject: [PATCH 03/30] Add grid node anchor correct for calculate borders --- druid/base/grid.lua | 14 +++++++++----- example/gui/main/main.gui | 2 +- example/page/infinity_page.lua | 4 ++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/druid/base/grid.lua b/druid/base/grid.lua index fd4fe48..908a921 100644 --- a/druid/base/grid.lua +++ b/druid/base/grid.lua @@ -43,7 +43,10 @@ function M.init(self, parent, element, in_row) self.anchor = vmath.vector3(0.5 + pivot.x, 0.5 - pivot.y, 0) self.in_row = in_row or 1 - self.node_size = gui.get_size(self:get_node(element)) + local node = self:get_node(element) + self.node_size = gui.get_size(node) + self.node_pivot = const.PIVOTS[gui.get_pivot(node)] + self.border = vmath.vector4(0) self.border_offset = vmath.vector3(0) @@ -58,11 +61,12 @@ end local function _update_border(self, pos, border) local size = self.node_size + local pivot = self.node_pivot - local left = pos.x - size.x/2 + self.border_offset.x - local right = pos.x + size.x/2 + self.border_offset.x - local top = pos.y + size.y/2 + self.border_offset.y - local bottom = pos.y - size.y/2 + self.border_offset.y + local left = pos.x - size.x/2 - (size.x * pivot.x) + self.border_offset.x + local right = pos.x + size.x/2 - (size.x * pivot.x) + self.border_offset.x + local top = pos.y + size.y/2 - (size.y * pivot.y) + self.border_offset.y + local bottom = pos.y - size.y/2 - (size.y * pivot.y)+ self.border_offset.y border.x = math.min(border.x, left) border.y = math.max(border.y, top) diff --git a/example/gui/main/main.gui b/example/gui/main/main.gui index d5bce3c..329796d 100644 --- a/example/gui/main/main.gui +++ b/example/gui/main/main.gui @@ -10676,7 +10676,7 @@ nodes { nodes { position { x: 0.0 - y: 3.0 + y: 4.0 z: 0.0 w: 1.0 } diff --git a/example/page/infinity_page.lua b/example/page/infinity_page.lua index 133f26f..7b6c6cf 100644 --- a/example/page/infinity_page.lua +++ b/example/page/infinity_page.lua @@ -17,7 +17,7 @@ end local function setup_infinity_list(self) local data = {} - for i = 1, 2500 do + for i = 1, 250 do table.insert(data, i) end @@ -27,7 +27,7 @@ local function setup_infinity_list(self) end) -- scroll to some index - local pos = self.infinity_grid:get_pos(950) + local pos = self.infinity_grid:get_pos(100) self.infinity_scroll:scroll_to(pos, true) end From 2d2790f760b6c0f8a582d0d58f831a35788efb8c Mon Sep 17 00:00:00 2001 From: Insality Date: Wed, 16 Sep 2020 01:23:11 +0300 Subject: [PATCH 04/30] Add more infinity scroll example --- druid/base/grid.lua | 6 +- druid/base/infinity_list.lua | 5 +- example/gui/main/main.gui | 238 ++++++++++++++++++++++++++++++++- example/page/infinity_page.lua | 25 ++++ 4 files changed, 265 insertions(+), 9 deletions(-) diff --git a/druid/base/grid.lua b/druid/base/grid.lua index 908a921..8fe45d4 100644 --- a/druid/base/grid.lua +++ b/druid/base/grid.lua @@ -125,8 +125,10 @@ function M.get_index(self, pos) local col = (pos.x + self.border_offset.x) / (self.node_size.x + self.offset.x) local row = -(pos.y + self.border_offset.y) / (self.node_size.y + self.offset.y) - local index = col + (row * self.in_row) + 1 - return math.floor(index) + row = math.floor(row) + + local index = col + (row * self.in_row) + return math.ceil(index) end diff --git a/druid/base/infinity_list.lua b/druid/base/infinity_list.lua index b44f522..ca5fb95 100644 --- a/druid/base/infinity_list.lua +++ b/druid/base/infinity_list.lua @@ -23,7 +23,7 @@ function M:init(data_list, scroll, grid, create_function) self.components = {} self.elements_view_count = vmath.vector3( - math.ceil(self.view_size.x / self.prefab_size.x), + math.min(math.ceil(self.view_size.x / self.prefab_size.x), self.grid.in_row), math.ceil(self.view_size.y / self.prefab_size.y), 0) @@ -89,8 +89,9 @@ end function M:_check_elements() local pos = gui.get_position(self.scroll.content_node) pos.y = -pos.y + local top_index = self.grid:get_index(pos) - local last_index = top_index + (self.elements_view_count.x * self.elements_view_count.y) + 1 + local last_index = top_index + (self.elements_view_count.x * self.elements_view_count.y) + self.grid.in_row - 1 -- Clear outside elements for index, _ in pairs(self.nodes) do diff --git a/example/gui/main/main.gui b/example/gui/main/main.gui index 329796d..28d3c77 100644 --- a/example/gui/main/main.gui +++ b/example/gui/main/main.gui @@ -10511,7 +10511,7 @@ nodes { nodes { position { x: 0.0 - y: 200.0 + y: 280.0 z: 0.0 w: 1.0 } @@ -10529,7 +10529,7 @@ nodes { } size { x: 300.0 - y: 400.0 + y: 250.0 z: 0.0 w: 1.0 } @@ -10584,7 +10584,7 @@ nodes { } size { x: 400.0 - y: 400.0 + y: 250.0 z: 0.0 w: 1.0 } @@ -10621,7 +10621,7 @@ nodes { nodes { position { x: 0.0 - y: 160.0 + y: 240.0 z: 0.0 w: 1.0 } @@ -10739,7 +10739,7 @@ nodes { nodes { position { x: 0.0 - y: 240.0 + y: 320.0 z: 0.0 w: 1.0 } @@ -10799,6 +10799,234 @@ nodes { text_leading: 1.0 text_tracking: 0.0 } +nodes { + position { + x: 0.0 + y: -44.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 270.0 + y: 250.0 + z: 0.0 + w: 1.0 + } + color { + x: 0.8 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "" + id: "infinity_scroll_3_stencil" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_N + adjust_mode: ADJUST_MODE_FIT + parent: "infinity_page" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_STENCIL + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 270.0 + y: 250.0 + z: 0.0 + w: 1.0 + } + color { + x: 0.8 + y: 1.0 + z: 0.8 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "infinity_scroll_3_content" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_N + adjust_mode: ADJUST_MODE_FIT + parent: "infinity_scroll_3_stencil" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: -106.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 90.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/button_blue" + id: "infinity_prefab_small" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "infinity_page" + layer: "" + inherit_alpha: true + slice9 { + x: 20.0 + y: 0.0 + z: 20.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 4.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.75 + y: 0.75 + z: 1.0 + w: 1.0 + } + size { + x: 380.0 + y: 50.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "001" + font: "game" + id: "infinity_text_3" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.3019608 + y: 0.4 + z: 0.8 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "infinity_prefab_small" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} nodes { position { x: 0.0 diff --git a/example/page/infinity_page.lua b/example/page/infinity_page.lua index 7b6c6cf..2fc10e9 100644 --- a/example/page/infinity_page.lua +++ b/example/page/infinity_page.lua @@ -14,6 +14,19 @@ local function create_infinity_instance(self, record, index) end +local function create_infinity_instance_small(self, record, index) + local instance = gui.clone_tree(self.infinity_prefab_small) + gui.set_enabled(instance["infinity_prefab_small"], true) + gui.set_text(instance["infinity_text_3"], index) + + local button = self.druid:new_button(instance["infinity_prefab_small"], function() + print("Infinity click on", index) + end) + + return instance["infinity_prefab_small"], button +end + + local function setup_infinity_list(self) local data = {} @@ -29,6 +42,12 @@ local function setup_infinity_list(self) -- scroll to some index local pos = self.infinity_grid:get_pos(100) self.infinity_scroll:scroll_to(pos, true) + + + self.infinity_list_small = self.druid:new_infinity_list(data, self.infinity_scroll_3, self.infinity_grid_3, function(record, index) + -- function should return gui_node, [druid_component] + return create_infinity_instance_small(self, record, index) + end) end @@ -40,6 +59,12 @@ function M.setup_page(self) self.infinity_prefab = gui.get_node("infinity_prefab") gui.set_enabled(self.infinity_prefab, false) + self.infinity_scroll_3 = self.druid:new_scroll("infinity_scroll_3_stencil", "infinity_scroll_3_content") + self.infinity_grid_3 = self.druid:new_grid("infinity_scroll_3_content", "infinity_prefab_small", 3) + + self.infinity_prefab_small = gui.get_node("infinity_prefab_small") + gui.set_enabled(self.infinity_prefab_small, false) + setup_infinity_list(self) end From 2ef2a61dfac27d94a1d65a65d02eb3ef274c3aa1 Mon Sep 17 00:00:00 2001 From: Insality Date: Tue, 3 Nov 2020 23:25:59 +0300 Subject: [PATCH 05/30] Merge fixes --- druid/system/druid_instance.lua | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/druid/system/druid_instance.lua b/druid/system/druid_instance.lua index 2e8b907..d66f543 100644 --- a/druid/system/druid_instance.lua +++ b/druid/system/druid_instance.lua @@ -524,27 +524,15 @@ function DruidInstance.new_checkbox_group(self, nodes, callback, click_nodes) end -<<<<<<< HEAD ---- Create drag basic component --- @function druid:new_drag --- @tparam args ... drag init args --- @treturn Component drag component -function Druid.new_drag(self, ...) - return Druid.create(self, drag, ...) -end - - --- Create infinity list basic component -- @function druid:new_infinity_list -- @tparam args ... drag init args --- @treturn Component drag component -function Druid.new_infinity_list(self, ...) - return Druid.create(self, infinity_list, ...) +-- @treturn Component infinity list component +function DruidInstance.new_infinity_list(self, ...) + return DruidInstance.create(self, infinity_list, ...) end -return Druid -======= --- Create radio_group component -- @tparam DruidInstance self -- @tparam node[] nodes Array of gui node @@ -583,4 +571,3 @@ end return DruidInstance ->>>>>>> develop From 3bbb4129c6d86bf38733d067773b1be8706971e4 Mon Sep 17 00:00:00 2001 From: Insality Date: Sat, 7 Nov 2020 22:15:46 +0300 Subject: [PATCH 06/30] Add get_offset for grids, add offset for scroll content --- druid/base/scroll.lua | 21 ++++++++++------ druid/base/static_grid.lua | 44 ++++++++++++++++++++++++--------- druid/extended/dynamic_grid.lua | 38 ++++++++++++++++------------ 3 files changed, 68 insertions(+), 35 deletions(-) diff --git a/druid/base/scroll.lua b/druid/base/scroll.lua index 7f76bb6..7d6b4d9 100644 --- a/druid/base/scroll.lua +++ b/druid/base/scroll.lua @@ -69,14 +69,17 @@ end --- Update vector with next conditions: -- Field x have to <= field z -- Field y have to <= field w -local function get_border_vector(vector) +local function get_border_vector(vector, offset) if vector.x > vector.z then vector.x, vector.z = vector.z, vector.x end if vector.y > vector.w then vector.y, vector.w = vector.w, vector.y end - + vector.x = vector.x - offset.x + vector.z = vector.z - offset.x + vector.y = vector.y - offset.y + vector.w = vector.w - offset.y return vector end @@ -145,6 +148,7 @@ function Scroll.init(self, view_node, content_node) self.selected = nil self.is_animate = false + self._offset = vmath.vector3(0) self._is_horizontal_scroll = true self._is_vertical_scroll = true self._grid_on_change = nil @@ -262,7 +266,10 @@ end -- @tparam Scroll self -- @tparam vector3 size The new size for content node -- @treturn druid.scroll Current scroll instance -function Scroll.set_size(self, size) +function Scroll.set_size(self, size, offset) + if offset then + self._offset = offset + end gui.set_size(self.content_node, size) self:_update_size() @@ -372,9 +379,9 @@ function Scroll.bind_grid(self, grid) self._grid_on_change = grid.on_change_items self._grid_on_change_callback = self._grid_on_change:subscribe(function() - self:set_size(grid:get_size()) + self:set_size(grid:get_size(), grid:get_offset()) end) - self:set_size(grid:get_size()) + self:set_size(grid:get_size(), grid:get_offset()) return self end @@ -598,7 +605,7 @@ function Scroll._update_size(self) local content_border = helper.get_border(self.content_node) local content_size = vmath.mul_per_elem(gui.get_size(self.content_node), gui.get_scale(self.content_node)) - self.available_pos = get_border_vector(view_border - content_border) + self.available_pos = get_border_vector(view_border - content_border, self._offset) self.available_size = get_size_vector(self.available_pos) self.drag.can_x = self.available_size.x > 0 and self._is_horizontal_scroll @@ -627,7 +634,7 @@ function Scroll._update_size(self) self.drag.can_y = content_size.y > view_size.y end - self.available_pos_extra = get_border_vector(view_border - content_border_extra) + self.available_pos_extra = get_border_vector(view_border - content_border_extra, self._offset) self.available_size_extra = get_size_vector(self.available_pos_extra) end diff --git a/druid/base/static_grid.lua b/druid/base/static_grid.lua index 86f1c13..cbc5dda 100644 --- a/druid/base/static_grid.lua +++ b/druid/base/static_grid.lua @@ -49,6 +49,19 @@ local component = require("druid.component") local StaticGrid = component.create("static_grid", { const.ON_LAYOUT_CHANGE }) +local function _extend_border(border, pos, size, pivot) + local left = pos.x - size.x/2 - (size.x * pivot.x) + local right = pos.x + size.x/2 - (size.x * pivot.x) + local top = pos.y + size.y/2 - (size.y * pivot.y) + local bottom = pos.y - size.y/2 - (size.y * pivot.y) + + border.x = math.min(border.x, left) + border.y = math.max(border.y, top) + border.z = math.max(border.z, right) + border.w = math.min(border.w, bottom) +end + + --- Component init function -- @tparam StaticGrid self -- @tparam node parent The gui node parent, where items will be placed @@ -66,6 +79,7 @@ function StaticGrid.init(self, parent, element, in_row) self._prefab = self:get_node(element) self.node_size = gui.get_size(self._prefab) self.node_pivot = const.PIVOTS[gui.get_pivot(self._prefab)] + self.grid_zero_y = self.node_size.y * self.pivot.y -- Y pos at first grid line self.border = vmath.vector4(0) -- Current grid content size @@ -209,6 +223,14 @@ function StaticGrid.get_size(self) end +--- Return grid content borders +-- @tparam StaticGrid self +-- @treturn vector3 The grid content borders +function StaticGrid.get_borders(self) + return self.border +end + + --- Return array of all node positions -- @tparam StaticGrid self -- @treturn vector3[] All grid node positions @@ -262,12 +284,20 @@ function StaticGrid._get_zero_offset(self) -- zero offset: center pos - border size * anchor return vmath.vector3( -((self.border.x + self.border.z)/2 + (self.border.z - self.border.x) * self.pivot.x), - -((self.border.y + self.border.w)/2 + (self.border.y - self.border.w) * self.pivot.y), + -((self.grid_zero_y + self.border.w)/2 + (self.grid_zero_y - self.border.w) * self.pivot.y), 0 ) end +-- return vector where content borders starts +function StaticGrid:get_offset() + local zero_offset = self:_get_zero_offset() + local borders = self:get_borders() + return vmath.vector3(0, zero_offset.y + borders.y, 0) +end + + --- Update grid inner state -- @tparam StaticGrid self -- @tparam bool is_instant If true, node position update instantly, otherwise with set_position_function callback @@ -309,17 +339,7 @@ function StaticGrid._update_borders(self) local size = self.node_size local pivot = self.node_pivot for index, node in pairs(self.nodes) do - local pos = self:get_pos(index) - - local left = pos.x - size.x/2 - (size.x * pivot.x) - local right = pos.x + size.x/2 - (size.x * pivot.x) - local top = pos.y + size.y/2 - (size.y * pivot.y) - local bottom = pos.y - size.y/2 - (size.y * pivot.y) - - self.border.x = math.min(self.border.x, left) - self.border.y = math.max(self.border.y, top) - self.border.z = math.max(self.border.z, right) - self.border.w = math.min(self.border.w, bottom) + _extend_border(self.border, self:get_pos(index), size, pivot) end end diff --git a/druid/extended/dynamic_grid.lua b/druid/extended/dynamic_grid.lua index e2b618f..91b14c3 100644 --- a/druid/extended/dynamic_grid.lua +++ b/druid/extended/dynamic_grid.lua @@ -21,7 +21,7 @@ --- Parent gui node -- @tfield node parent ---- List of all grid nodes +--- List of all grid elements. Contains from node, pos, size, pivot -- @tfield node[] nodes --- The first index of node in grid @@ -220,6 +220,14 @@ function DynamicGrid.get_size(self, border) end +--- Return grid content borders +-- @tparam DynamicGrid self +-- @treturn vector3 The grid content borders +function DynamicGrid.get_borders(self) + return self.border +end + + --- Return grid index by node -- @tparam DynamicGrid self -- @tparam node node The gui node in the grid @@ -283,7 +291,7 @@ function DynamicGrid._add_node(self, node, index, origin_index) -- Add new item instantly in new pos gui.set_parent(node, self.parent) - gui.set_position(node, self.nodes[index].pos + self:_get_zero_offset()) + gui.set_position(node, self.nodes[index].pos) end @@ -348,13 +356,11 @@ end -- @tparam bool is_instant If true, node position update instantly, otherwise with set_position_function callback -- @local function DynamicGrid._update_pos(self, is_instant) - local offset = self:_get_zero_offset() - for index, node in pairs(self.nodes) do if is_instant then - gui.set_position(node.node, node.pos + offset) + gui.set_position(node.node, node.pos) else - self._set_position_function(node.node, node.pos + offset) + self._set_position_function(node.node, node.pos) end end @@ -386,17 +392,17 @@ function DynamicGrid._get_node_size(self, node) end ---- Return elements offset for correct posing nodes. Correct posing at --- parent pivot node (0:0) with adjusting of node sizes and anchoring --- @tparam DynamicGrid self --- @treturn vector3 The offset vector --- @local -function DynamicGrid._get_zero_offset(self) - -- zero offset: center pos - border size * anchor - return vmath.vector3( - -((self.border.x + self.border.z)/2 + (self.border.z - self.border.x) * self.pivot.x), - -((self.border.y + self.border.w)/2 + (self.border.y - self.border.w) * self.pivot.y), +function DynamicGrid:get_offset() + -- return vector where content borders starts + local size = self:get_size() + local borders = self:get_borders() + local offset = vmath.vector3( + (borders.z + borders.x)/2 + size.x * self.pivot.x, + (borders.y + borders.w)/2 + size.y * self.pivot.y, + 0, 0) + + return offset end From 00aff868409c58380d9f55b8344296a59d0d26b5 Mon Sep 17 00:00:00 2001 From: Insality Date: Sat, 7 Nov 2020 22:16:40 +0300 Subject: [PATCH 07/30] #86 set minimal step for scroll free intertion --- druid/base/scroll.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/druid/base/scroll.lua b/druid/base/scroll.lua index 7d6b4d9..d286f0c 100644 --- a/druid/base/scroll.lua +++ b/druid/base/scroll.lua @@ -435,19 +435,23 @@ function Scroll._check_soft_zone(self) -- Right border (minimum x) if target.x < border.x then - target.x = helper.step(target.x, border.x, math.abs(target.x - border.x) * speed) + local step = math.max(math.abs(target.x - border.x) * speed, 1) + target.x = helper.step(target.x, border.x, step) end -- Left border (maximum x) if target.x > border.z then - target.x = helper.step(target.x, border.z, math.abs(target.x - border.z) * speed) + local step = math.max(math.abs(target.x - border.z) * speed, 1) + target.x = helper.step(target.x, border.z, step) end -- Top border (maximum y) if target.y < border.y then - target.y = helper.step(target.y, border.y, math.abs(target.y - border.y) * speed) + local step = math.max(math.abs(target.y - border.y) * speed, 1) + target.y = helper.step(target.y, border.y, step) end -- Bot border (minimum y) if target.y > border.w then - target.y = helper.step(target.y, border.w, math.abs(target.y - border.w) * speed) + local step = math.max(math.abs(target.y - border.w) * speed, 1) + target.y = helper.step(target.y, border.w, step) end end From 6ec46545ecf94db2a45f51fb06750360a46c4e8e Mon Sep 17 00:00:00 2001 From: Insality Date: Sun, 8 Nov 2020 00:53:35 +0300 Subject: [PATCH 08/30] Add static grid get_size_for function --- druid/base/static_grid.lua | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/druid/base/static_grid.lua b/druid/base/static_grid.lua index cbc5dda..33bd2c8 100644 --- a/druid/base/static_grid.lua +++ b/druid/base/static_grid.lua @@ -223,6 +223,27 @@ function StaticGrid.get_size(self) end + +function StaticGrid.get_size_for(self, count) + if count == 0 then + return vmath.vector3(0) + end + + local border = vmath.vector4(math.huge, -math.huge, -math.huge, math.huge) + + local size = self.node_size + local pivot = self.node_pivot + _extend_border(border, self:get_pos(1), size, pivot) + _extend_border(border, self:get_pos(self.in_row), size, pivot) + _extend_border(border, self:get_pos(count), size, pivot) + + return vmath.vector3( + border.z - border.x, + border.y - border.w, + 0) +end + + --- Return grid content borders -- @tparam StaticGrid self -- @treturn vector3 The grid content borders From 7da1e1fab0d395b54c1748db1c7bd580d0b59826 Mon Sep 17 00:00:00 2001 From: Insality Date: Sun, 8 Nov 2020 00:53:52 +0300 Subject: [PATCH 09/30] Infinity scroll works for static grid --- druid/extended/infinity_list.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/druid/extended/infinity_list.lua b/druid/extended/infinity_list.lua index 8848e53..43f7a36 100644 --- a/druid/extended/infinity_list.lua +++ b/druid/extended/infinity_list.lua @@ -89,8 +89,8 @@ function M:_check_elements() local pos = gui.get_position(self.scroll.content_node) pos.y = -pos.y - local top_index = self.grid:get_index(pos) - local last_index = top_index + (self.elements_view_count.x * self.elements_view_count.y) + self.grid.in_row - 1 + local top_index = self.grid:get_index(pos) - self.grid.in_row + local last_index = (top_index - 1) + (self.elements_view_count.x * self.elements_view_count.y) + self.grid.in_row -- Clear outside elements for index, _ in pairs(self.nodes) do @@ -109,7 +109,7 @@ end function M:_recalc_scroll_size() - local element_size = self.grid:get_size_for_elements_count(#self.data) + local element_size = self.grid:get_size_for(#self.data) self.scroll:set_size(element_size) end From 4730da768f9d4ae84f74bb575c8afad64b611bcf Mon Sep 17 00:00:00 2001 From: Insality Date: Sun, 8 Nov 2020 00:54:20 +0300 Subject: [PATCH 10/30] Update infinity page example script --- example/page/infinity_page.lua | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/example/page/infinity_page.lua b/example/page/infinity_page.lua index 2fc10e9..a98636d 100644 --- a/example/page/infinity_page.lua +++ b/example/page/infinity_page.lua @@ -1,4 +1,4 @@ -local M = {} + local M = {} local function create_infinity_instance(self, record, index) @@ -30,7 +30,7 @@ end local function setup_infinity_list(self) local data = {} - for i = 1, 250 do + for i = 1, 50 do table.insert(data, i) end @@ -40,7 +40,7 @@ local function setup_infinity_list(self) end) -- scroll to some index - local pos = self.infinity_grid:get_pos(100) + local pos = self.infinity_grid:get_pos(25) self.infinity_scroll:scroll_to(pos, true) @@ -53,15 +53,14 @@ end function M.setup_page(self) self.infinity_scroll = self.druid:new_scroll("infinity_scroll_stencil", "infinity_scroll_content") - self.infinity_grid = self.druid:new_grid("infinity_scroll_content", "infinity_prefab", 1) - self.infinity_grid:set_offset(vmath.vector3(0, 8, 0)) - + :set_horizontal_scroll(false) + self.infinity_grid = self.druid:new_static_grid("infinity_scroll_content", "infinity_prefab", 1) self.infinity_prefab = gui.get_node("infinity_prefab") gui.set_enabled(self.infinity_prefab, false) self.infinity_scroll_3 = self.druid:new_scroll("infinity_scroll_3_stencil", "infinity_scroll_3_content") - self.infinity_grid_3 = self.druid:new_grid("infinity_scroll_3_content", "infinity_prefab_small", 3) - + :set_horizontal_scroll(false) + self.infinity_grid_3 = self.druid:new_static_grid("infinity_scroll_3_content", "infinity_prefab_small", 3) self.infinity_prefab_small = gui.get_node("infinity_prefab_small") gui.set_enabled(self.infinity_prefab_small, false) From ad59c30a89f0a449f6263076863e6203944ddf05 Mon Sep 17 00:00:00 2001 From: Insality Date: Sun, 8 Nov 2020 13:34:15 +0300 Subject: [PATCH 11/30] Add scroll is_node_in_view function --- druid/base/scroll.lua | 17 +++++++++++++++++ druid/helper.lua | 13 +++++++++++-- example/page/grid_page.lua | 1 + 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/druid/base/scroll.lua b/druid/base/scroll.lua index d286f0c..c9a3465 100644 --- a/druid/base/scroll.lua +++ b/druid/base/scroll.lua @@ -359,6 +359,23 @@ function Scroll.set_vertical_scroll(self, state) end +function Scroll.is_node_in_view(self, node) + local node_border = helper.get_border(node, gui.get_position(node)) + local view_border = helper.get_border(self.view_node, -self.position) + + -- Check is vertical outside (Left or Right): + if node_border.z < view_border.x or node_border.x > view_border.z then + return false + end + + -- Check is horizontal outside (Up or Down): + if node_border.w > view_border.y or node_border.y < view_border.w then + return false + end + + return true +end + --- Bind the grid component (Static or Dynamic) to recalculate -- scroll size on grid changes diff --git a/druid/helper.lua b/druid/helper.lua index c4c0041..4cf245a 100644 --- a/druid/helper.lua +++ b/druid/helper.lua @@ -186,16 +186,25 @@ end --- Distance from node to size border -- @function helper.get_border -- @return vector4 (left, top, right, down) -function M.get_border(node) +function M.get_border(node, offset) local pivot = gui.get_pivot(node) local pivot_offset = M.get_pivot_offset(pivot) local size = vmath.mul_per_elem(gui.get_size(node), gui.get_scale(node)) - return vmath.vector4( + local border = vmath.vector4( -size.x*(0.5 + pivot_offset.x), size.y*(0.5 - pivot_offset.y), size.x*(0.5 - pivot_offset.x), -size.y*(0.5 + pivot_offset.y) ) + + if offset then + border.x = border.x + offset.x + border.y = border.y + offset.y + border.z = border.z + offset.x + border.w = border.w + offset.y + end + + return border end diff --git a/example/page/grid_page.lua b/example/page/grid_page.lua index 4eba0c1..41ed7d5 100644 --- a/example/page/grid_page.lua +++ b/example/page/grid_page.lua @@ -25,6 +25,7 @@ local function add_node(self, index) local prefab = gui.get_node("grid_nodes_prefab") local cloned = gui.clone_tree(prefab) gui.set_enabled(cloned["grid_nodes_prefab"], true) + gui.set_text(cloned["grid_nodes_text"], index) local button = self.druid:new_button(cloned["grid_nodes_prefab"], function(_, params, button) remove_node(self, button, true) From 560c6cb95f0320262de15e7f017dd1b2f4b96361 Mon Sep 17 00:00:00 2001 From: Insality Date: Sun, 8 Nov 2020 13:58:05 +0300 Subject: [PATCH 12/30] Start implement new check elements (for both dynamic and static grids). Add dynnamic infinityi scroll to example --- druid/extended/dynamic_grid.lua | 8 + druid/extended/infinity_list.lua | 76 +++++- example/gui/main/main.gui | 449 +++++++++++++++++++++++++------ example/page/grid_page.lua | 3 +- example/page/infinity_page.lua | 31 ++- 5 files changed, 474 insertions(+), 93 deletions(-) diff --git a/druid/extended/dynamic_grid.lua b/druid/extended/dynamic_grid.lua index 91b14c3..2dc0fbc 100644 --- a/druid/extended/dynamic_grid.lua +++ b/druid/extended/dynamic_grid.lua @@ -220,6 +220,14 @@ function DynamicGrid.get_size(self, border) end +function DynamicGrid.get_size_for(self, count) + return vmath.vector3( + self.border.z - self.border.x, + self.border.y - self.border.w, + 0) +end + + --- Return grid content borders -- @tparam DynamicGrid self -- @treturn vector3 The grid content borders diff --git a/druid/extended/infinity_list.lua b/druid/extended/infinity_list.lua index 43f7a36..190000a 100644 --- a/druid/extended/infinity_list.lua +++ b/druid/extended/infinity_list.lua @@ -15,17 +15,13 @@ function M:init(data_list, scroll, grid, create_function) self.data = data_list self.top_index = 1 + self.last_index = 1 self.create_function = create_function self.nodes = {} self.components = {} - self.elements_view_count = vmath.vector3( - math.min(math.ceil(self.view_size.x / self.prefab_size.x), self.grid.in_row), - math.ceil(self.view_size.y / self.prefab_size.y), - 0) - self:_refresh() self.scroll.on_scroll:subscribe(function() self._check_elements(self) end) end @@ -85,7 +81,7 @@ function M:_refresh() end -function M:_check_elements() +function M:_check_elements_old() local pos = gui.get_position(self.scroll.content_node) pos.y = -pos.y @@ -108,6 +104,74 @@ function M:_check_elements() end +function M:_check_elements() + local top_index = self.top_index + self.last_index = self.top_index + + for index, node in pairs(self.nodes) do + if self.scroll:is_node_in_view(node) then + top_index = index + break + end + end + + -- make items from (top_index upside + local is_top_outside = false + local cur_index = top_index - 1 + while not is_top_outside do + if not self.data[cur_index] then + break + end + + if not self.nodes[cur_index] then + self:_add_at(cur_index) + end + + if not self.scroll:is_node_in_view(self.nodes[cur_index]) then + is_top_outside = true + + -- remove nexts: + local remove_index = cur_index - 1 + while self.nodes[remove_index] do + self:_remove_at(remove_index) + remove_index = remove_index - 1 + end + end + + cur_index = cur_index - 1 + end + + -- make items from [top_index downsize + local is_bot_outside = false + cur_index = top_index + while not is_bot_outside do + if not self.data[cur_index] then + break + end + + if not self.nodes[cur_index] then + self:_add_at(cur_index) + end + if not self.scroll:is_node_in_view(self.nodes[cur_index]) then + is_bot_outside = true + + -- remove nexts: + local remove_index = cur_index + 1 + while self.nodes[remove_index] do + self:_remove_at(remove_index) + remove_index = remove_index + 1 + end + else + self.last_index = cur_index + end + + cur_index = cur_index + 1 + end + + self.top_index = top_index +end + + function M:_recalc_scroll_size() local element_size = self.grid:get_size_for(#self.data) self.scroll:set_size(element_size) diff --git a/example/gui/main/main.gui b/example/gui/main/main.gui index fd0159f..7e191c3 100644 --- a/example/gui/main/main.gui +++ b/example/gui/main/main.gui @@ -9832,7 +9832,7 @@ nodes { w: 1.0 } size { - x: 400.0 + x: 500.0 y: 400.0 z: 0.0 w: 1.0 @@ -9887,15 +9887,15 @@ nodes { w: 1.0 } size { - x: 400.0 + x: 500.0 y: 400.0 z: 0.0 w: 1.0 } color { - x: 1.0 - y: 1.0 - z: 1.0 + x: 0.5019608 + y: 0.3019608 + z: 0.5019608 w: 1.0 } type: TYPE_BOX @@ -11216,9 +11216,9 @@ nodes { w: 1.0 } color { - x: 1.0 - y: 1.0 - z: 1.0 + x: 0.8 + y: 0.4 + z: 0.2 w: 1.0 } type: TYPE_BOX @@ -11638,8 +11638,8 @@ nodes { w: 1.0 } size { - x: 1.0 - y: 1.0 + x: 600.0 + y: 900.0 z: 0.0 w: 1.0 } @@ -11671,12 +11671,12 @@ nodes { clipping_inverted: false alpha: 1.0 template_node_child: false - size_mode: SIZE_MODE_AUTO + size_mode: SIZE_MODE_MANUAL } nodes { position { x: 0.0 - y: 280.0 + y: 450.0 z: 0.0 w: 1.0 } @@ -11693,7 +11693,125 @@ nodes { w: 1.0 } size { - x: 300.0 + x: 600.0 + y: 2000.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "infinity_page_content" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_N + adjust_mode: ADJUST_MODE_FIT + parent: "infinity_page" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: -128.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 400.0 + y: 50.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Infinity scroll:" + font: "game" + id: "infinity_header" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "infinity_page_content" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 0.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 0.0 + y: -644.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 400.0 y: 250.0 z: 0.0 w: 1.0 @@ -11712,7 +11830,7 @@ nodes { yanchor: YANCHOR_NONE pivot: PIVOT_N adjust_mode: ADJUST_MODE_FIT - parent: "infinity_page" + parent: "infinity_page_content" layer: "" inherit_alpha: true slice9 { @@ -11786,7 +11904,7 @@ nodes { nodes { position { x: 0.0 - y: 240.0 + y: -684.0 z: 0.0 w: 1.0 } @@ -11822,7 +11940,7 @@ nodes { yanchor: YANCHOR_NONE pivot: PIVOT_CENTER adjust_mode: ADJUST_MODE_FIT - parent: "infinity_page" + parent: "infinity_page_content" layer: "" inherit_alpha: true slice9 { @@ -11904,7 +12022,7 @@ nodes { nodes { position { x: 0.0 - y: 320.0 + y: -968.0 z: 0.0 w: 1.0 } @@ -11922,69 +12040,6 @@ nodes { } size { x: 400.0 - y: 50.0 - z: 0.0 - w: 1.0 - } - color { - x: 1.0 - y: 1.0 - z: 1.0 - w: 1.0 - } - type: TYPE_TEXT - blend_mode: BLEND_MODE_ALPHA - text: "Infinity scroll:" - font: "game" - id: "infinity_header" - xanchor: XANCHOR_NONE - yanchor: YANCHOR_NONE - pivot: PIVOT_CENTER - outline { - x: 1.0 - y: 1.0 - z: 1.0 - w: 1.0 - } - shadow { - x: 1.0 - y: 1.0 - z: 1.0 - w: 1.0 - } - adjust_mode: ADJUST_MODE_FIT - line_break: false - parent: "infinity_page" - layer: "" - inherit_alpha: true - alpha: 1.0 - outline_alpha: 0.0 - shadow_alpha: 0.0 - template_node_child: false - text_leading: 1.0 - text_tracking: 0.0 -} -nodes { - position { - x: 0.0 - y: -44.0 - z: 0.0 - w: 1.0 - } - rotation { - x: 0.0 - y: 0.0 - z: 0.0 - w: 1.0 - } - scale { - x: 1.0 - y: 1.0 - z: 1.0 - w: 1.0 - } - size { - x: 270.0 y: 250.0 z: 0.0 w: 1.0 @@ -12003,7 +12058,7 @@ nodes { yanchor: YANCHOR_NONE pivot: PIVOT_N adjust_mode: ADJUST_MODE_FIT - parent: "infinity_page" + parent: "infinity_page_content" layer: "" inherit_alpha: true slice9 { @@ -12077,7 +12132,7 @@ nodes { nodes { position { x: 0.0 - y: -106.0 + y: -1030.0 z: 0.0 w: 1.0 } @@ -12113,7 +12168,7 @@ nodes { yanchor: YANCHOR_NONE pivot: PIVOT_CENTER adjust_mode: ADJUST_MODE_FIT - parent: "infinity_page" + parent: "infinity_page_content" layer: "" inherit_alpha: true slice9 { @@ -12192,6 +12247,234 @@ nodes { text_leading: 1.0 text_tracking: 0.0 } +nodes { + position { + x: 0.0 + y: -176.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 400.0 + y: 400.0 + z: 0.0 + w: 1.0 + } + color { + x: 0.8 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "" + id: "infinity_scroll_stencil_dynamic" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_N + adjust_mode: ADJUST_MODE_FIT + parent: "infinity_page_content" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_STENCIL + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 400.0 + y: 400.0 + z: 0.0 + w: 1.0 + } + color { + x: 0.8 + y: 1.0 + z: 0.8 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "infinity_scroll_content_dynamic" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_N + adjust_mode: ADJUST_MODE_FIT + parent: "infinity_scroll_stencil_dynamic" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: -216.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 300.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/button_blue" + id: "infinity_prefab_dynamic" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "infinity_page_content" + layer: "" + inherit_alpha: true + slice9 { + x: 20.0 + y: 0.0 + z: 20.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 4.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.75 + y: 0.75 + z: 1.0 + w: 1.0 + } + size { + x: 380.0 + y: 50.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Dynamic element 1" + font: "game" + id: "infinity_text_dynamic" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.3019608 + y: 0.4 + z: 0.8 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "infinity_prefab_dynamic" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} nodes { position { x: 0.0 diff --git a/example/page/grid_page.lua b/example/page/grid_page.lua index 41ed7d5..8b0dc06 100644 --- a/example/page/grid_page.lua +++ b/example/page/grid_page.lua @@ -25,7 +25,6 @@ local function add_node(self, index) local prefab = gui.get_node("grid_nodes_prefab") local cloned = gui.clone_tree(prefab) gui.set_enabled(cloned["grid_nodes_prefab"], true) - gui.set_text(cloned["grid_nodes_text"], index) local button = self.druid:new_button(cloned["grid_nodes_prefab"], function(_, params, button) remove_node(self, button, true) @@ -149,7 +148,7 @@ end function M.setup_page(self) - self.grid_page_scroll = self.druid:new_scroll("grid_page", "grid_page_content") + self.druid:new_scroll("grid_page", "grid_page_content") self.grid_static_grid = self.druid:new_static_grid("grid_nodes", "grid_nodes_prefab", 5) :set_position_function(simple_animate) diff --git a/example/page/infinity_page.lua b/example/page/infinity_page.lua index a98636d..71bc250 100644 --- a/example/page/infinity_page.lua +++ b/example/page/infinity_page.lua @@ -28,6 +28,20 @@ end +local function create_infinity_instance_dynamic(self, record, index) + local instance = gui.clone_tree(self.infinity_prefab_dynamic) + gui.set_enabled(instance["infinity_prefab_dynamic"], true) + gui.set_text(instance["infinity_text_dynamic"], "Dynamic record " .. index) + + gui.set_size(instance["infinity_prefab_dynamic"], vmath.vector3(300, 60 + index * 5, 0)) + local button = self.druid:new_button(instance["infinity_prefab_dynamic"], function() + print("Dynamic click on", index) + end) + + return instance["infinity_prefab_dynamic"], button +end + + local function setup_infinity_list(self) local data = {} for i = 1, 50 do @@ -40,18 +54,25 @@ local function setup_infinity_list(self) end) -- scroll to some index - local pos = self.infinity_grid:get_pos(25) - self.infinity_scroll:scroll_to(pos, true) + -- local pos = self.infinity_grid:get_pos(25) + -- self.infinity_scroll:scroll_to(pos, true) self.infinity_list_small = self.druid:new_infinity_list(data, self.infinity_scroll_3, self.infinity_grid_3, function(record, index) -- function should return gui_node, [druid_component] return create_infinity_instance_small(self, record, index) end) + + self.infinity_list_dynamic = self.druid:new_infinity_list(data, self.infinity_scroll_dynamic, self.infinity_grid_dynamic, function(record, index) + -- function should return gui_node, [druid_component] + return create_infinity_instance_dynamic(self, record, index) + end) end function M.setup_page(self) + self.druid:new_scroll("infinity_page", "infinity_page_content") + self.infinity_scroll = self.druid:new_scroll("infinity_scroll_stencil", "infinity_scroll_content") :set_horizontal_scroll(false) self.infinity_grid = self.druid:new_static_grid("infinity_scroll_content", "infinity_prefab", 1) @@ -64,6 +85,12 @@ function M.setup_page(self) self.infinity_prefab_small = gui.get_node("infinity_prefab_small") gui.set_enabled(self.infinity_prefab_small, false) + self.infinity_scroll_dynamic = self.druid:new_scroll("infinity_scroll_stencil_dynamic", "infinity_scroll_content_dynamic") + :set_horizontal_scroll(false) + self.infinity_grid_dynamic = self.druid:new_dynamic_grid("infinity_scroll_content_dynamic", "infinity_prefab", 1) + self.infinity_prefab_dynamic = gui.get_node("infinity_prefab_dynamic") + gui.set_enabled(self.infinity_prefab_dynamic, false) + setup_infinity_list(self) end From 74efdbfe714bbc318661ecbf8a3a36b54a5b6fed Mon Sep 17 00:00:00 2001 From: Insality Date: Sun, 8 Nov 2020 23:20:16 +0300 Subject: [PATCH 13/30] Add shift policy to grids --- druid/base/static_grid.lua | 28 ++++++++++++++++++++------- druid/const.lua | 7 +++++++ druid/extended/dynamic_grid.lua | 33 +++++++++++++++++--------------- druid/extended/infinity_list.lua | 13 +++++++++++-- 4 files changed, 57 insertions(+), 24 deletions(-) diff --git a/druid/base/static_grid.lua b/druid/base/static_grid.lua index 33bd2c8..f965a14 100644 --- a/druid/base/static_grid.lua +++ b/druid/base/static_grid.lua @@ -159,13 +159,21 @@ end -- @tparam StaticGrid self -- @tparam node item Gui node -- @tparam[opt] number index The item position. By default add as last item -function StaticGrid.add(self, item, index) +-- @tparam[opt=SHIFT.RIGHT] number shift_policy How shift nodes, if required. See const.SHIFT +function StaticGrid.add(self, item, index, shift_policy) + shift_policy = shift_policy or const.SHIFT.RIGHT index = index or ((self.last_index or 0) + 1) if self.nodes[index] then - -- Move nodes to right - for i = self.last_index, index, -1 do - self.nodes[i + 1] = self.nodes[i] + if shift_policy == const.SHIFT.RIGHT then + for i = self.last_index, index, -1 do + self.nodes[i + 1] = self.nodes[i] + end + end + if shift_policy == const.SHIFT.LEFT then + for i = self.first_index, index do + self.nodes[i - 1] = self.nodes[i] + end end end @@ -189,19 +197,25 @@ end --- Remove the item from the grid. Note that gui node will be not deleted -- @tparam StaticGrid self -- @tparam number index The grid node index to remove --- @tparam bool is_shift_nodes If true, will shift nodes left after index +-- @tparam[opt=SHIFT.RIGHT] number shift_policy How shift nodes, if required. See const.SHIFT -- @treturn Node The deleted gui node from grid -function StaticGrid.remove(self, index, is_shift_nodes) +function StaticGrid.remove(self, index, shift_policy) + shift_policy = shift_policy or const.SHIFT.RIGHT assert(self.nodes[index], "No grid item at given index " .. index) local remove_node = self.nodes[index] self.nodes[index] = nil - if is_shift_nodes then + if shift_policy == const.SHIFT.RIGHT then for i = index, self.last_index do self.nodes[i] = self.nodes[i + 1] end end + if shift_policy == const.SHIFT.LEFT then + for i = index, self.first_index, -1 do + self.nodes[i] = self.nodes[i - 1] + end + end self:_update() diff --git a/druid/const.lua b/druid/const.lua index a16499e..f8016bd 100644 --- a/druid/const.lua +++ b/druid/const.lua @@ -89,6 +89,13 @@ M.OS = { } +M.SHIFT = { + NO_SHIFT = 0, + LEFT = -1, + RIGHT = 1, +} + + M.SIDE = { X = "x", Y = "y" diff --git a/druid/extended/dynamic_grid.lua b/druid/extended/dynamic_grid.lua index 2dc0fbc..d1f1d9c 100644 --- a/druid/extended/dynamic_grid.lua +++ b/druid/extended/dynamic_grid.lua @@ -136,18 +136,19 @@ end -- @tparam DynamicGrid self -- @tparam node node Gui node -- @tparam[opt] number index The node position. By default add as last node --- @tparam[opt=false] bool is_shift_left If true, shift all nodes to the left, otherwise shift nodes to the right -function DynamicGrid.add(self, node, index, is_shift_left) - local delta = is_shift_left and -1 or 1 +-- @tparam[opt=SHIFT.RIGHT] number shift_policy How shift nodes, if required. See const.SHIFT +function DynamicGrid.add(self, node, index, shift_policy) + shift_policy = shift_policy or const.SHIFT.RIGHT + local delta = shift_policy -- -1 or 1 or 0 -- By default add node at end index = index or ((self.last_index or 0) + 1) -- If node exist at index place, shifting them - local is_shift = self.nodes[index] + local is_shift = self.nodes[index] and shift_policy ~= const.SHIFT.NO_SHIFT if is_shift then -- We need to iterate from index to start or end grid, depends of shift side - local start_index = is_shift_left and self.first_index or self.last_index + local start_index = shift_policy == const.SHIFT.LEFT and self.first_index or self.last_index for i = start_index, index, -delta do self.nodes[i + delta] = self.nodes[i] end @@ -158,14 +159,13 @@ function DynamicGrid.add(self, node, index, is_shift_left) -- After shifting we should recalc node poses if is_shift then -- We need to iterate from placed node to start or end grid, depends of shift side - local target_index = is_shift_left and self.first_index or self.last_index + local target_index = shift_policy == const.SHIFT.LEFT and self.first_index or self.last_index for i = index + delta, target_index + delta, delta do local move_node = self.nodes[i] move_node.pos = self:get_pos(i, move_node.node, i - delta) end end - -- Sync grid data self:_update() @@ -178,9 +178,11 @@ end -- @tparam DynamicGrid self -- @tparam number index The grid node index to remove -- @tparam[opt=false] bool is_shift_left If true, shift all nodes to the left, otherwise shift nodes to the right +-- @tparam[opt=SHIFT.RIGHT] number shift_policy How shift nodes, if required. See const.SHIFT -- @treturn Node The deleted gui node from grid -function DynamicGrid.remove(self, index, is_shift_left) - local delta = is_shift_left and -1 or 1 +function DynamicGrid.remove(self, index, shift_policy) + shift_policy = shift_policy or const.SHIFT.RIGHT + local delta = shift_policy -- -1 or 1 or 0 assert(self.nodes[index], "No grid item at given index " .. index) @@ -189,11 +191,13 @@ function DynamicGrid.remove(self, index, is_shift_left) self.nodes[index] = nil -- After delete node, we should shift nodes and recalc their poses, depends from is_shift_left - local target_index = is_shift_left and self.first_index or self.last_index - for i = index, target_index, delta do - self.nodes[i] = self.nodes[i + delta] - if self.nodes[i] then - self.nodes[i].pos = self:get_pos(i, self.nodes[i].node, i - delta) + if shift_policy ~= const.SHIFT.NO_SHIFT then + local target_index = shift_policy == const.SHIFT.LEFT and self.first_index or self.last_index + for i = index, target_index, delta do + self.nodes[i] = self.nodes[i + delta] + if self.nodes[i] then + self.nodes[i].pos = self:get_pos(i, self.nodes[i].node, i - delta) + end end end @@ -407,7 +411,6 @@ function DynamicGrid:get_offset() local offset = vmath.vector3( (borders.z + borders.x)/2 + size.x * self.pivot.x, (borders.y + borders.w)/2 + size.y * self.pivot.y, - 0, 0) return offset diff --git a/druid/extended/infinity_list.lua b/druid/extended/infinity_list.lua index 190000a..5f2e7c0 100644 --- a/druid/extended/infinity_list.lua +++ b/druid/extended/infinity_list.lua @@ -1,6 +1,7 @@ --- Manage data for huge dataset in scroll --- It requires basic druid scroll and druid grid components local const = require("druid.const") +local helper = require("druid.helper") local component = require("druid.component") local M = component.create("infinity_list", { const.ON_UPDATE }) @@ -12,6 +13,7 @@ function M:init(data_list, scroll, grid, create_function) self.druid = self:get_druid() self.scroll = scroll self.grid = grid + self.scroll:bind_grid(grid) self.data = data_list self.top_index = 1 @@ -46,20 +48,27 @@ function M:set_data(data_list) end +function M:scroll_to_index(index) + self.top_index = helper.clamp(index, 1, #self.data) + self:_refresh() + self.scroll.on_scroll:trigger(self:get_context(), self) +end + + function M:_add_at(index) if self.nodes[index] then self:_remove_at(index) end local node, instance = self.create_function(self.data[index], index) - self.grid:add(node, index) + self.grid:add(node, index, const.SHIFT.NO_SHIFT) self.nodes[index] = node self.components[index] = instance end function M:_remove_at(index) - self.grid:remove(index) + self.grid:remove(index, const.SHIFT.NO_SHIFT) local node = self.nodes[index] gui.delete_node(node) From e038d70039e86964c4da53c52e655a5161912e27 Mon Sep 17 00:00:00 2001 From: Insality Date: Sun, 8 Nov 2020 23:22:33 +0300 Subject: [PATCH 14/30] Return grid binding --- druid/extended/dynamic_grid.lua | 8 ------- druid/extended/infinity_list.lua | 39 +++----------------------------- 2 files changed, 3 insertions(+), 44 deletions(-) diff --git a/druid/extended/dynamic_grid.lua b/druid/extended/dynamic_grid.lua index d1f1d9c..e4bcb7f 100644 --- a/druid/extended/dynamic_grid.lua +++ b/druid/extended/dynamic_grid.lua @@ -224,14 +224,6 @@ function DynamicGrid.get_size(self, border) end -function DynamicGrid.get_size_for(self, count) - return vmath.vector3( - self.border.z - self.border.x, - self.border.y - self.border.w, - 0) -end - - --- Return grid content borders -- @tparam DynamicGrid self -- @treturn vector3 The grid content borders diff --git a/druid/extended/infinity_list.lua b/druid/extended/infinity_list.lua index 5f2e7c0..42f877f 100644 --- a/druid/extended/infinity_list.lua +++ b/druid/extended/infinity_list.lua @@ -86,47 +86,22 @@ function M:_refresh() self:_remove_at(index) end self:_check_elements() - self:_recalc_scroll_size() -end - - -function M:_check_elements_old() - local pos = gui.get_position(self.scroll.content_node) - pos.y = -pos.y - - local top_index = self.grid:get_index(pos) - self.grid.in_row - local last_index = (top_index - 1) + (self.elements_view_count.x * self.elements_view_count.y) + self.grid.in_row - - -- Clear outside elements - for index, _ in pairs(self.nodes) do - if index < top_index or index > last_index then - self:_remove_at(index) - end - end - - -- Spawn current elements - for index = top_index, last_index do - if self.data[index] and not self.nodes[index] then - self:_add_at(index) - end - end end function M:_check_elements() - local top_index = self.top_index self.last_index = self.top_index for index, node in pairs(self.nodes) do if self.scroll:is_node_in_view(node) then - top_index = index + self.top_index = index break end end -- make items from (top_index upside local is_top_outside = false - local cur_index = top_index - 1 + local cur_index = self.top_index - 1 while not is_top_outside do if not self.data[cur_index] then break @@ -152,7 +127,7 @@ function M:_check_elements() -- make items from [top_index downsize local is_bot_outside = false - cur_index = top_index + cur_index = self.top_index while not is_bot_outside do if not self.data[cur_index] then break @@ -176,14 +151,6 @@ function M:_check_elements() cur_index = cur_index + 1 end - - self.top_index = top_index -end - - -function M:_recalc_scroll_size() - local element_size = self.grid:get_size_for(#self.data) - self.scroll:set_size(element_size) end From 834d0714e0da4ed5e4727459da9bd730f62fe4a1 Mon Sep 17 00:00:00 2001 From: Insality Date: Mon, 9 Nov 2020 01:11:22 +0300 Subject: [PATCH 15/30] Fix static poses calculation --- druid/base/static_grid.lua | 49 +++-- example/gui/main/main.gui | 340 +++++++++++++++++++++++++++------ example/page/grid_page.lua | 16 +- example/page/infinity_page.lua | 58 ++++-- 4 files changed, 358 insertions(+), 105 deletions(-) diff --git a/druid/base/static_grid.lua b/druid/base/static_grid.lua index f965a14..7dd090f 100644 --- a/druid/base/static_grid.lua +++ b/druid/base/static_grid.lua @@ -79,10 +79,16 @@ function StaticGrid.init(self, parent, element, in_row) self._prefab = self:get_node(element) self.node_size = gui.get_size(self._prefab) self.node_pivot = const.PIVOTS[gui.get_pivot(self._prefab)] - self.grid_zero_y = self.node_size.y * self.pivot.y -- Y pos at first grid line + + self._grid_horizonal_offset = self.node_size.x * (self.in_row - 1) * self.anchor.x + self._zero_offset = vmath.vector3( + self.node_size.x * self.node_pivot.x - self.node_size.x * self.pivot.x - self._grid_horizonal_offset, + self.node_size.y * self.node_pivot.y - self.node_size.y * self.pivot.y, + 0) self.border = vmath.vector4(0) -- Current grid content size + self.on_add_item = Event() self.on_remove_item = Event() self.on_change_items = Event() @@ -102,8 +108,8 @@ function StaticGrid.get_pos(self, index) local row = math.ceil(index / self.in_row) - 1 local col = (index - row * self.in_row) - 1 - _temp_pos.x = col * self.node_size.x - _temp_pos.y = -row * self.node_size.y + _temp_pos.x = col * self.node_size.x + self._zero_offset.x + _temp_pos.y = -row * self.node_size.y + self._zero_offset.y _temp_pos.z = 0 return _temp_pos @@ -185,7 +191,7 @@ function StaticGrid.add(self, item, index, shift_policy) self:_update_indexes() self:_update_borders() - gui.set_position(item, self:get_pos(index) + self:_get_zero_offset()) + gui.set_position(item, self:get_pos(index)) self:_update_pos() @@ -239,7 +245,7 @@ end function StaticGrid.get_size_for(self, count) - if count == 0 then + if not count or count == 0 then return vmath.vector3(0) end @@ -248,8 +254,10 @@ function StaticGrid.get_size_for(self, count) local size = self.node_size local pivot = self.node_pivot _extend_border(border, self:get_pos(1), size, pivot) - _extend_border(border, self:get_pos(self.in_row), size, pivot) _extend_border(border, self:get_pos(count), size, pivot) + if count >= self.in_row then + _extend_border(border, self:get_pos(self.in_row), size, pivot) + end return vmath.vector3( border.z - border.x, @@ -310,26 +318,17 @@ function StaticGrid.clear(self) end ---- Return elements offset for correct posing nodes. Correct posing at --- parent pivot node (0:0) with adjusting of node sizes and anchoring --- @tparam StaticGrid self --- @treturn vector3 The offset vector --- @local -function StaticGrid._get_zero_offset(self) - -- zero offset: center pos - border size * anchor - return vmath.vector3( - -((self.border.x + self.border.z)/2 + (self.border.z - self.border.x) * self.pivot.x), - -((self.grid_zero_y + self.border.w)/2 + (self.grid_zero_y - self.border.w) * self.pivot.y), - 0 - ) -end - - -- return vector where content borders starts function StaticGrid:get_offset() - local zero_offset = self:_get_zero_offset() local borders = self:get_borders() - return vmath.vector3(0, zero_offset.y + borders.y, 0) + local size = self:get_size() + + local offset = vmath.vector3( + (borders.z + borders.x)/2 + size.x * self.pivot.x, + (borders.y + borders.w)/2 + size.y * self.pivot.y, + 0) + + return offset end @@ -384,12 +383,8 @@ end -- @tparam bool is_instant If true, node position update instantly, otherwise with set_position_function callback -- @local function StaticGrid._update_pos(self, is_instant) - local zero_offset = self:_get_zero_offset() - for i, node in pairs(self.nodes) do local pos = self:get_pos(i) - pos.x = pos.x + zero_offset.x - pos.y = pos.y + zero_offset.y if is_instant then gui.set_position(node, pos) diff --git a/example/gui/main/main.gui b/example/gui/main/main.gui index 7e191c3..04e4f78 100644 --- a/example/gui/main/main.gui +++ b/example/gui/main/main.gui @@ -11793,7 +11793,7 @@ nodes { } nodes { position { - x: 0.0 + x: -150.0 y: -644.0 z: 0.0 w: 1.0 @@ -11811,7 +11811,7 @@ nodes { w: 1.0 } size { - x: 400.0 + x: 250.0 y: 250.0 z: 0.0 w: 1.0 @@ -11866,7 +11866,7 @@ nodes { w: 1.0 } size { - x: 400.0 + x: 250.0 y: 250.0 z: 0.0 w: 1.0 @@ -11879,7 +11879,7 @@ nodes { } type: TYPE_BOX blend_mode: BLEND_MODE_ALPHA - texture: "kenney/empty" + texture: "" id: "infinity_scroll_content" xanchor: XANCHOR_NONE yanchor: YANCHOR_NONE @@ -11903,8 +11903,8 @@ nodes { } nodes { position { - x: 0.0 - y: -684.0 + x: 150.0 + y: -644.0 z: 0.0 w: 1.0 } @@ -11921,35 +11921,35 @@ nodes { w: 1.0 } size { - x: 300.0 - y: 60.0 + x: 250.0 + y: 100.0 z: 0.0 w: 1.0 } color { - x: 1.0 + x: 0.8 y: 1.0 z: 1.0 w: 1.0 } type: TYPE_BOX blend_mode: BLEND_MODE_ALPHA - texture: "kenney/button_blue" - id: "infinity_prefab" + texture: "" + id: "infinity_scroll_stencil_hor" xanchor: XANCHOR_NONE yanchor: YANCHOR_NONE - pivot: PIVOT_CENTER + pivot: PIVOT_N adjust_mode: ADJUST_MODE_FIT parent: "infinity_page_content" layer: "" inherit_alpha: true slice9 { - x: 20.0 + x: 0.0 y: 0.0 - z: 20.0 + z: 0.0 w: 0.0 } - clipping_mode: CLIPPING_MODE_NONE + clipping_mode: CLIPPING_MODE_STENCIL clipping_visible: true clipping_inverted: false alpha: 1.0 @@ -11958,8 +11958,8 @@ nodes { } nodes { position { - x: 0.0 - y: 4.0 + x: -125.0 + y: -50.0 z: 0.0 w: 1.0 } @@ -11970,54 +11970,46 @@ nodes { w: 1.0 } scale { - x: 0.75 - y: 0.75 + x: 1.0 + y: 1.0 z: 1.0 w: 1.0 } size { - x: 380.0 - y: 50.0 + x: 250.0 + y: 100.0 z: 0.0 w: 1.0 } color { - x: 1.0 + x: 0.8 y: 1.0 - z: 1.0 - w: 1.0 - } - type: TYPE_TEXT - blend_mode: BLEND_MODE_ALPHA - text: "Infinity element 1" - font: "game" - id: "infinity_text" - xanchor: XANCHOR_NONE - yanchor: YANCHOR_NONE - pivot: PIVOT_CENTER - outline { - x: 0.3019608 - y: 0.4 z: 0.8 w: 1.0 } - shadow { - x: 1.0 - y: 1.0 - z: 1.0 - w: 1.0 - } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "" + id: "infinity_scroll_content_hor" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W adjust_mode: ADJUST_MODE_FIT - line_break: false - parent: "infinity_prefab" + parent: "infinity_scroll_stencil_hor" layer: "" inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false alpha: 1.0 - outline_alpha: 1.0 - shadow_alpha: 0.0 template_node_child: false - text_leading: 1.0 - text_tracking: 0.0 + size_mode: SIZE_MODE_MANUAL } nodes { position { @@ -12204,7 +12196,7 @@ nodes { w: 1.0 } size { - x: 380.0 + x: 60.0 y: 50.0 z: 0.0 w: 1.0 @@ -12249,7 +12241,7 @@ nodes { } nodes { position { - x: 0.0 + x: -150.0 y: -176.0 z: 0.0 w: 1.0 @@ -12267,8 +12259,8 @@ nodes { w: 1.0 } size { - x: 400.0 - y: 400.0 + x: 250.0 + y: 350.0 z: 0.0 w: 1.0 } @@ -12322,8 +12314,8 @@ nodes { w: 1.0 } size { - x: 400.0 - y: 400.0 + x: 250.0 + y: 350.0 z: 0.0 w: 1.0 } @@ -12359,8 +12351,8 @@ nodes { } nodes { position { - x: 0.0 - y: -216.0 + x: 150.0 + y: -176.0 z: 0.0 w: 1.0 } @@ -12377,7 +12369,117 @@ nodes { w: 1.0 } size { - x: 300.0 + x: 250.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 0.8 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "" + id: "infinity_scroll_stencil_dynamic_hor" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_N + adjust_mode: ADJUST_MODE_FIT + parent: "infinity_page_content" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_STENCIL + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: -125.0 + y: -50.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 250.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 0.8 + y: 1.0 + z: 0.8 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "infinity_scroll_content_dynamic_hor" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + adjust_mode: ADJUST_MODE_FIT + parent: "infinity_scroll_stencil_dynamic_hor" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 150.0 + y: -820.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 y: 60.0 z: 0.0 w: 1.0 @@ -12391,7 +12493,7 @@ nodes { type: TYPE_BOX blend_mode: BLEND_MODE_ALPHA texture: "kenney/button_blue" - id: "infinity_prefab_dynamic" + id: "infinity_prefab" xanchor: XANCHOR_NONE yanchor: YANCHOR_NONE pivot: PIVOT_CENTER @@ -12432,7 +12534,7 @@ nodes { w: 1.0 } size { - x: 380.0 + x: 200.0 y: 50.0 z: 0.0 w: 1.0 @@ -12445,7 +12547,125 @@ nodes { } type: TYPE_TEXT blend_mode: BLEND_MODE_ALPHA - text: "Dynamic element 1" + text: "Record 1" + font: "game" + id: "infinity_text" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.3019608 + y: 0.4 + z: 0.8 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "infinity_prefab" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 151.0 + y: -356.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/button_blue" + id: "infinity_prefab_dynamic" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "infinity_page_content" + layer: "" + inherit_alpha: true + slice9 { + x: 20.0 + y: 10.0 + z: 20.0 + w: 20.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 4.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.75 + y: 0.75 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 50.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Dynamic 1" font: "game" id: "infinity_text_dynamic" xanchor: XANCHOR_NONE diff --git a/example/page/grid_page.lua b/example/page/grid_page.lua index 8b0dc06..1bac0a2 100644 --- a/example/page/grid_page.lua +++ b/example/page/grid_page.lua @@ -1,3 +1,5 @@ +local const = require("druid.const") + local M = {} @@ -6,12 +8,12 @@ local function simple_animate(node, pos) end -local function remove_node(self, button, is_shift) +local function remove_node(self, button, no_shift) gui.delete_node(button.node) self.druid:remove(button) local index = self.grid_static_grid:get_index_by_node(button.node) - self.grid_static_grid:remove(index, is_shift) + self.grid_static_grid:remove(index, no_shift and const.SHIFT.NO_SHIFT or const.SHIFT.RIGHT) for i = 1, #self.grid_node_buttons do if self.grid_node_buttons[i] == button then table.remove(self.grid_node_buttons, i) @@ -27,10 +29,10 @@ local function add_node(self, index) gui.set_enabled(cloned["grid_nodes_prefab"], true) local button = self.druid:new_button(cloned["grid_nodes_prefab"], function(_, params, button) - remove_node(self, button, true) + remove_node(self, button) end) button.on_long_click:subscribe(function() - remove_node(self, button) + remove_node(self, button, true) end) button:set_click_zone(self.grid_static_scroll.view_node) @@ -72,12 +74,12 @@ local function init_static_grid(self) end -local function remove_dynamic_node(self, button, is_shift_left) +local function remove_dynamic_node(self, button, shift_policy) gui.delete_node(button.node) self.druid:remove(button) local index = self.grid_dynamic_grid:get_index_by_node(button.node) - self.grid_dynamic_grid:remove(index, is_shift_left) + self.grid_dynamic_grid:remove(index, shift_policy) for i = 1, #self.dynamic_node_buttons do if self.dynamic_node_buttons[i] == button then table.remove(self.dynamic_node_buttons, i) @@ -97,7 +99,7 @@ local function add_node_dynamic(self, index, is_shift_left) remove_dynamic_node(self, button) end) button.on_long_click:subscribe(function() - remove_dynamic_node(self, button, true) + remove_dynamic_node(self, button, const.SHIFT.lEFT) end) button:set_click_zone(self.grid_dynamic_scroll.view_node) table.insert(self.dynamic_node_buttons, button) diff --git a/example/page/infinity_page.lua b/example/page/infinity_page.lua index 71bc250..bae7279 100644 --- a/example/page/infinity_page.lua +++ b/example/page/infinity_page.lua @@ -4,7 +4,7 @@ local function create_infinity_instance(self, record, index) local instance = gui.clone_tree(self.infinity_prefab) gui.set_enabled(instance["infinity_prefab"], true) - gui.set_text(instance["infinity_text"], "Infinity record " .. index) + gui.set_text(instance["infinity_text"], "Record " .. index) local button = self.druid:new_button(instance["infinity_prefab"], function() print("Infinity click on", index) @@ -27,13 +27,26 @@ local function create_infinity_instance_small(self, record, index) end - local function create_infinity_instance_dynamic(self, record, index) local instance = gui.clone_tree(self.infinity_prefab_dynamic) gui.set_enabled(instance["infinity_prefab_dynamic"], true) - gui.set_text(instance["infinity_text_dynamic"], "Dynamic record " .. index) + gui.set_text(instance["infinity_text_dynamic"], "Record " .. index) - gui.set_size(instance["infinity_prefab_dynamic"], vmath.vector3(300, 60 + index * 5, 0)) + gui.set_size(instance["infinity_prefab_dynamic"], vmath.vector3(200, 60 + index * 3, 0)) + local button = self.druid:new_button(instance["infinity_prefab_dynamic"], function() + print("Dynamic click on", index) + end) + + return instance["infinity_prefab_dynamic"], button +end + + +local function create_infinity_instance_dynamic_hor(self, record, index) + local instance = gui.clone_tree(self.infinity_prefab_dynamic) + gui.set_enabled(instance["infinity_prefab_dynamic"], true) + gui.set_text(instance["infinity_text_dynamic"], "Record " .. index) + + gui.set_size(instance["infinity_prefab_dynamic"], vmath.vector3(150 + 2 * index, 60, 0)) local button = self.druid:new_button(instance["infinity_prefab_dynamic"], function() print("Dynamic click on", index) end) @@ -53,9 +66,17 @@ local function setup_infinity_list(self) return create_infinity_instance(self, record, index) end) + self.infinity_list_hor = self.druid:new_infinity_list(data, self.infinity_scroll_hor, self.infinity_grid_hor, function(record, index) + -- function should return gui_node, [druid_component] + return create_infinity_instance(self, record, index) + end) + -- scroll to some index -- local pos = self.infinity_grid:get_pos(25) -- self.infinity_scroll:scroll_to(pos, true) + -- timer.delay(1, false, function() + -- self.infinity_list:scroll_to_index(1) + -- end) self.infinity_list_small = self.druid:new_infinity_list(data, self.infinity_scroll_3, self.infinity_grid_3, function(record, index) @@ -67,29 +88,44 @@ local function setup_infinity_list(self) -- function should return gui_node, [druid_component] return create_infinity_instance_dynamic(self, record, index) end) + + self.infinity_list_dynamic_hor = self.druid:new_infinity_list(data, self.infinity_scroll_dynamic_hor, self.infinity_grid_dynamic_hor, function(record, index) + -- function should return gui_node, [druid_component] + return create_infinity_instance_dynamic_hor(self, record, index) + end) end function M.setup_page(self) self.druid:new_scroll("infinity_page", "infinity_page_content") + self.infinity_prefab = gui.get_node("infinity_prefab") + self.infinity_prefab_small = gui.get_node("infinity_prefab_small") + self.infinity_prefab_dynamic = gui.get_node("infinity_prefab_dynamic") + gui.set_enabled(self.infinity_prefab, false) + gui.set_enabled(self.infinity_prefab_small, false) + gui.set_enabled(self.infinity_prefab_dynamic, false) + self.infinity_scroll = self.druid:new_scroll("infinity_scroll_stencil", "infinity_scroll_content") :set_horizontal_scroll(false) self.infinity_grid = self.druid:new_static_grid("infinity_scroll_content", "infinity_prefab", 1) - self.infinity_prefab = gui.get_node("infinity_prefab") - gui.set_enabled(self.infinity_prefab, false) + + self.infinity_scroll_hor = self.druid:new_scroll("infinity_scroll_stencil_hor", "infinity_scroll_content_hor") + :set_vertical_scroll(false) + self.infinity_grid_hor = self.druid:new_static_grid("infinity_scroll_content_hor", "infinity_prefab", 999) self.infinity_scroll_3 = self.druid:new_scroll("infinity_scroll_3_stencil", "infinity_scroll_3_content") :set_horizontal_scroll(false) self.infinity_grid_3 = self.druid:new_static_grid("infinity_scroll_3_content", "infinity_prefab_small", 3) - self.infinity_prefab_small = gui.get_node("infinity_prefab_small") - gui.set_enabled(self.infinity_prefab_small, false) self.infinity_scroll_dynamic = self.druid:new_scroll("infinity_scroll_stencil_dynamic", "infinity_scroll_content_dynamic") :set_horizontal_scroll(false) - self.infinity_grid_dynamic = self.druid:new_dynamic_grid("infinity_scroll_content_dynamic", "infinity_prefab", 1) - self.infinity_prefab_dynamic = gui.get_node("infinity_prefab_dynamic") - gui.set_enabled(self.infinity_prefab_dynamic, false) + self.infinity_grid_dynamic = self.druid:new_dynamic_grid("infinity_scroll_content_dynamic") + + self.infinity_scroll_dynamic_hor = self.druid:new_scroll("infinity_scroll_stencil_dynamic_hor", "infinity_scroll_content_dynamic_hor") + :set_vertical_scroll(false) + self.infinity_grid_dynamic_hor = self.druid:new_dynamic_grid("infinity_scroll_content_dynamic_hor") + setup_infinity_list(self) end From e9a8dd0795051c38dfce5c55d5e3643939ca6048 Mon Sep 17 00:00:00 2001 From: Insality Date: Sun, 29 Nov 2020 20:52:04 +0300 Subject: [PATCH 16/30] Optimize main example gui scene --- example/game.appmanifest | 5 ++- example/gui/main/main.gui | 54 ++++++++++++++++---------------- example/gui/main/main.gui_script | 4 +++ example/page/infinity_page.lua | 12 +++++-- 4 files changed, 42 insertions(+), 33 deletions(-) diff --git a/example/game.appmanifest b/example/game.appmanifest index 93bc543..c16be5a 100644 --- a/example/game.appmanifest +++ b/example/game.appmanifest @@ -3,9 +3,8 @@ platforms: x86_64-osx: context: - excludeLibs: ["physics","LinearMath","BulletDynamics","BulletCollision","Box2D","record","vpx","profilerext"] - excludeSymbols: ["ProfilerExt"] - libs: ["physics_null","record_null","profilerext_null"] + excludeLibs: ["physics","LinearMath","BulletDynamics","BulletCollision","Box2D","record","vpx"] + libs: ["physics_null","record_null"] linkFlags: [] x86_64-linux: diff --git a/example/gui/main/main.gui b/example/gui/main/main.gui index 04e4f78..748cfc9 100644 --- a/example/gui/main/main.gui +++ b/example/gui/main/main.gui @@ -3149,7 +3149,7 @@ nodes { type: TYPE_BOX blend_mode: BLEND_MODE_ALPHA texture: "kenney/empty" - id: "text_page" + id: "texts_page" xanchor: XANCHOR_NONE yanchor: YANCHOR_NONE pivot: PIVOT_CENTER @@ -3223,7 +3223,7 @@ nodes { } adjust_mode: ADJUST_MODE_FIT line_break: false - parent: "text_page" + parent: "texts_page" layer: "text" inherit_alpha: true alpha: 1.0 @@ -3286,7 +3286,7 @@ nodes { } adjust_mode: ADJUST_MODE_FIT line_break: false - parent: "text_page" + parent: "texts_page" layer: "text" inherit_alpha: true alpha: 1.0 @@ -3349,7 +3349,7 @@ nodes { } adjust_mode: ADJUST_MODE_FIT line_break: false - parent: "text_page" + parent: "texts_page" layer: "text" inherit_alpha: true alpha: 1.0 @@ -3412,7 +3412,7 @@ nodes { } adjust_mode: ADJUST_MODE_FIT line_break: false - parent: "text_page" + parent: "texts_page" layer: "text" inherit_alpha: true alpha: 1.0 @@ -3475,7 +3475,7 @@ nodes { } adjust_mode: ADJUST_MODE_FIT line_break: false - parent: "text_page" + parent: "texts_page" layer: "text" inherit_alpha: true alpha: 1.0 @@ -3538,7 +3538,7 @@ nodes { } adjust_mode: ADJUST_MODE_FIT line_break: false - parent: "text_page" + parent: "texts_page" layer: "text" inherit_alpha: true alpha: 1.0 @@ -3601,7 +3601,7 @@ nodes { } adjust_mode: ADJUST_MODE_FIT line_break: false - parent: "text_page" + parent: "texts_page" layer: "text" inherit_alpha: true alpha: 1.0 @@ -3664,7 +3664,7 @@ nodes { } adjust_mode: ADJUST_MODE_FIT line_break: false - parent: "text_page" + parent: "texts_page" layer: "text" inherit_alpha: true alpha: 1.0 @@ -3727,7 +3727,7 @@ nodes { } adjust_mode: ADJUST_MODE_FIT line_break: true - parent: "text_page" + parent: "texts_page" layer: "text" inherit_alpha: true alpha: 1.0 @@ -3776,7 +3776,7 @@ nodes { yanchor: YANCHOR_NONE pivot: PIVOT_CENTER adjust_mode: ADJUST_MODE_FIT - parent: "text_page" + parent: "texts_page" layer: "image" inherit_alpha: true slice9 { @@ -3845,7 +3845,7 @@ nodes { } adjust_mode: ADJUST_MODE_FIT line_break: false - parent: "text_page" + parent: "texts_page" layer: "text" inherit_alpha: true alpha: 1.0 @@ -3908,7 +3908,7 @@ nodes { } adjust_mode: ADJUST_MODE_FIT line_break: false - parent: "text_page" + parent: "texts_page" layer: "text" inherit_alpha: true alpha: 1.0 @@ -3971,7 +3971,7 @@ nodes { } adjust_mode: ADJUST_MODE_FIT line_break: false - parent: "text_page" + parent: "texts_page" layer: "text" inherit_alpha: true alpha: 1.0 @@ -4034,7 +4034,7 @@ nodes { } adjust_mode: ADJUST_MODE_FIT line_break: false - parent: "text_page" + parent: "texts_page" layer: "text" inherit_alpha: true alpha: 1.0 @@ -4083,7 +4083,7 @@ nodes { yanchor: YANCHOR_NONE pivot: PIVOT_CENTER adjust_mode: ADJUST_MODE_FIT - parent: "text_page" + parent: "texts_page" layer: "image" inherit_alpha: true slice9 { @@ -4152,7 +4152,7 @@ nodes { } adjust_mode: ADJUST_MODE_FIT line_break: true - parent: "text_page" + parent: "texts_page" layer: "text" inherit_alpha: true alpha: 1.0 @@ -10280,7 +10280,7 @@ nodes { pivot: PIVOT_CENTER adjust_mode: ADJUST_MODE_FIT parent: "info_grid_static" - layer: "" + layer: "image" inherit_alpha: true slice9 { x: 0.0 @@ -10335,7 +10335,7 @@ nodes { pivot: PIVOT_CENTER adjust_mode: ADJUST_MODE_FIT parent: "grid_nodes_prefab" - layer: "" + layer: "image" inherit_alpha: true slice9 { x: 0.0 @@ -10404,7 +10404,7 @@ nodes { adjust_mode: ADJUST_MODE_FIT line_break: false parent: "grid_nodes_dot" - layer: "" + layer: "text" inherit_alpha: true alpha: 1.0 outline_alpha: 0.0 @@ -10683,7 +10683,7 @@ nodes { pivot: PIVOT_CENTER adjust_mode: ADJUST_MODE_FIT parent: "info_grid_dynamic" - layer: "" + layer: "image" inherit_alpha: true slice9 { x: 20.0 @@ -12161,7 +12161,7 @@ nodes { pivot: PIVOT_CENTER adjust_mode: ADJUST_MODE_FIT parent: "infinity_page_content" - layer: "" + layer: "image" inherit_alpha: true slice9 { x: 20.0 @@ -12230,7 +12230,7 @@ nodes { adjust_mode: ADJUST_MODE_FIT line_break: false parent: "infinity_prefab_small" - layer: "" + layer: "text" inherit_alpha: true alpha: 1.0 outline_alpha: 1.0 @@ -12499,7 +12499,7 @@ nodes { pivot: PIVOT_CENTER adjust_mode: ADJUST_MODE_FIT parent: "infinity_page_content" - layer: "" + layer: "image" inherit_alpha: true slice9 { x: 20.0 @@ -12568,7 +12568,7 @@ nodes { adjust_mode: ADJUST_MODE_FIT line_break: false parent: "infinity_prefab" - layer: "" + layer: "text" inherit_alpha: true alpha: 1.0 outline_alpha: 1.0 @@ -12617,7 +12617,7 @@ nodes { pivot: PIVOT_CENTER adjust_mode: ADJUST_MODE_FIT parent: "infinity_page_content" - layer: "" + layer: "image" inherit_alpha: true slice9 { x: 20.0 @@ -12686,7 +12686,7 @@ nodes { adjust_mode: ADJUST_MODE_FIT line_break: false parent: "infinity_prefab_dynamic" - layer: "" + layer: "text" inherit_alpha: true alpha: 1.0 outline_alpha: 1.0 diff --git a/example/gui/main/main.gui_script b/example/gui/main/main.gui_script index 4f07c8d..7a4d589 100644 --- a/example/gui/main/main.gui_script +++ b/example/gui/main/main.gui_script @@ -31,6 +31,10 @@ local function on_control_button(self, delta) self.header:translate(pages[self.page]) local node = gui.get_node("C_Anchor") + + for i = 1, #pages do + gui.set_enabled(gui.get_node(pages[i]), i == self.page) + end gui.animate(node, "position.x", (self.page-1) * -600, gui.EASING_OUTSINE, 0.2) end diff --git a/example/page/infinity_page.lua b/example/page/infinity_page.lua index bae7279..085d2f9 100644 --- a/example/page/infinity_page.lua +++ b/example/page/infinity_page.lua @@ -22,6 +22,7 @@ local function create_infinity_instance_small(self, record, index) local button = self.druid:new_button(instance["infinity_prefab_small"], function() print("Infinity click on", index) end) + button:set_click_zone(self.infinity_scroll_3.view_node) return instance["infinity_prefab_small"], button end @@ -36,6 +37,7 @@ local function create_infinity_instance_dynamic(self, record, index) local button = self.druid:new_button(instance["infinity_prefab_dynamic"], function() print("Dynamic click on", index) end) + button:set_click_zone(self.infinity_scroll_dynamic.view_node) return instance["infinity_prefab_dynamic"], button end @@ -50,6 +52,7 @@ local function create_infinity_instance_dynamic_hor(self, record, index) local button = self.druid:new_button(instance["infinity_prefab_dynamic"], function() print("Dynamic click on", index) end) + button:set_click_zone(self.infinity_scroll_dynamic_hor.view_node) return instance["infinity_prefab_dynamic"], button end @@ -63,12 +66,16 @@ local function setup_infinity_list(self) self.infinity_list = self.druid:new_infinity_list(data, self.infinity_scroll, self.infinity_grid, function(record, index) -- function should return gui_node, [druid_component] - return create_infinity_instance(self, record, index) + local root, button = create_infinity_instance(self, record, index) + button:set_click_zone(self.infinity_scroll.view_node) + return root, button end) self.infinity_list_hor = self.druid:new_infinity_list(data, self.infinity_scroll_hor, self.infinity_grid_hor, function(record, index) -- function should return gui_node, [druid_component] - return create_infinity_instance(self, record, index) + local root, button = create_infinity_instance(self, record, index) + button:set_click_zone(self.infinity_scroll_hor.view_node) + return root, button end) -- scroll to some index @@ -126,7 +133,6 @@ function M.setup_page(self) :set_vertical_scroll(false) self.infinity_grid_dynamic_hor = self.druid:new_dynamic_grid("infinity_scroll_content_dynamic_hor") - setup_infinity_list(self) end From de5dad8874e54dfc9031e6d5d1445d9393727f3f Mon Sep 17 00:00:00 2001 From: Insality Date: Sun, 29 Nov 2020 21:37:42 +0300 Subject: [PATCH 17/30] Update infinity_list, add optional context to druid event --- druid/event.lua | 25 ++++--- druid/extended/infinity_list.lua | 112 +++++++++++++++++-------------- 2 files changed, 78 insertions(+), 59 deletions(-) diff --git a/druid/event.lua b/druid/event.lua index 0e9f624..5893cdc 100644 --- a/druid/event.lua +++ b/druid/event.lua @@ -22,11 +22,15 @@ end --- Subscribe callback on event -- @tparam DruidEvent self -- @tparam function callback Callback itself -function DruidEvent.subscribe(self, callback) +-- @tparam table context Additional context as first param to callback call +function DruidEvent.subscribe(self, callback, context) assert(type(self) == "table", "You should subscribe to event with : syntax") assert(type(callback) == "function", "Callback should be function") - table.insert(self._callbacks, callback) + table.insert(self._callbacks, { + callback = callback, + context = context + }) return callback end @@ -35,10 +39,11 @@ end --- Unsubscribe callback on event -- @tparam DruidEvent self -- @tparam function callback Callback itself -function DruidEvent.unsubscribe(self, callback) - for i = 1, #self._callbacks do - if self._callbacks[i] == callback then - table.remove(self._callbacks, i) +-- @tparam table context Additional context as first param to callback call +function DruidEvent.unsubscribe(self, callback, context) + for index, callback_info in ipairs(self._callbacks) do + if callback_info.callback == callback and callback_info.context == context then + table.remove(self._callbacks, index) return end end @@ -64,8 +69,12 @@ end -- @tparam DruidEvent self -- @tparam any ... All event params function DruidEvent.trigger(self, ...) - for i = 1, #self._callbacks do - self._callbacks[i](...) + for index, callback_info in ipairs(self._callbacks) do + if callback_info.context then + callback_info.callback(callback_info.context, ...) + else + callback_info.callback(...) + end end end diff --git a/druid/extended/infinity_list.lua b/druid/extended/infinity_list.lua index 42f877f..0ab82ac 100644 --- a/druid/extended/infinity_list.lua +++ b/druid/extended/infinity_list.lua @@ -4,7 +4,7 @@ local const = require("druid.const") local helper = require("druid.helper") local component = require("druid.component") -local M = component.create("infinity_list", { const.ON_UPDATE }) +local M = component.create("infinity_list") function M:init(data_list, scroll, grid, create_function) @@ -25,20 +25,12 @@ function M:init(data_list, scroll, grid, create_function) self.components = {} self:_refresh() - self.scroll.on_scroll:subscribe(function() self._check_elements(self) end) + self.scroll.on_scroll:subscribe(self._check_elements, self) end function M:on_remove() - -- TODO: make this work - -- self.scroll.on_scroll:unsubscribe(self._check_elements) -end - - -function M:update(dt) - if self.scroll.animate then - self:_check_elements() - end + self.scroll.on_scroll:unsubscribe(self._check_elements, self) end @@ -48,6 +40,44 @@ function M:set_data(data_list) end +function M:add(data, index) + table.insert(self.data, index, data) + self:_refresh() +end + + +function M:remove(index, shift_policy) + table.remove(self.data, index) + self:_refresh() +end + + +function M:clear() + self.data = {} + self:_refresh() +end + + +function M:get_first_index() + return self.top_index +end + + +function M:get_last_index() + return self.last_index +end + + +function M:get_index(data) + for index, value in pairs(self.data) do + if value == data then + return index + end + end + return nil +end + + function M:scroll_to_index(index) self.top_index = helper.clamp(index, 1, #self.data) self:_refresh() @@ -90,66 +120,46 @@ end function M:_check_elements() - self.last_index = self.top_index - for index, node in pairs(self.nodes) do if self.scroll:is_node_in_view(node) then self.top_index = index - break + self.last_index = index end end - -- make items from (top_index upside - local is_top_outside = false - local cur_index = self.top_index - 1 - while not is_top_outside do - if not self.data[cur_index] then - break - end + self:_check_elements_from(self.top_index - 1, -1) + self:_check_elements_from(self.top_index, 1) - if not self.nodes[cur_index] then - self:_add_at(cur_index) - end - - if not self.scroll:is_node_in_view(self.nodes[cur_index]) then - is_top_outside = true - - -- remove nexts: - local remove_index = cur_index - 1 - while self.nodes[remove_index] do - self:_remove_at(remove_index) - remove_index = remove_index - 1 - end - end - - cur_index = cur_index - 1 + for index, node in pairs(self.nodes) do + self.top_index = math.min(self.top_index or index, index) + self.last_index = math.max(self.last_index or index, index) end +end - -- make items from [top_index downsize - local is_bot_outside = false - cur_index = self.top_index - while not is_bot_outside do - if not self.data[cur_index] then + +function M:_check_elements_from(index, step) + local is_outside = false + while not is_outside do + if not self.data[index] then break end - if not self.nodes[cur_index] then - self:_add_at(cur_index) + if not self.nodes[index] then + self:_add_at(index) end - if not self.scroll:is_node_in_view(self.nodes[cur_index]) then - is_bot_outside = true + + if not self.scroll:is_node_in_view(self.nodes[index]) then + is_outside = true -- remove nexts: - local remove_index = cur_index + 1 + local remove_index = index while self.nodes[remove_index] do self:_remove_at(remove_index) - remove_index = remove_index + 1 + remove_index = remove_index + step end - else - self.last_index = cur_index end - cur_index = cur_index + 1 + index = index + step end end From dd3058db8389b0096e7176151a26d20d54e53218 Mon Sep 17 00:00:00 2001 From: Insality Date: Sun, 29 Nov 2020 21:37:56 +0300 Subject: [PATCH 18/30] Add toggle stencil in infinity scroll example --- example/gui/main/main.gui | 178 +++++++++++++++++++++++++++++++-- example/page/infinity_page.lua | 14 +++ 2 files changed, 183 insertions(+), 9 deletions(-) diff --git a/example/gui/main/main.gui b/example/gui/main/main.gui index 748cfc9..b8892d0 100644 --- a/example/gui/main/main.gui +++ b/example/gui/main/main.gui @@ -11731,7 +11731,167 @@ nodes { nodes { position { x: 0.0 - y: -128.0 + y: -157.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "button_toggle_stencil" + parent: "infinity_page_content" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/example/templates/button.gui" + template_node_child: false +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 220.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/button_blue" + id: "button_toggle_stencil/button" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "button_toggle_stencil" + layer: "image" + inherit_alpha: true + slice9 { + x: 15.0 + y: 15.0 + z: 15.0 + w: 15.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + overridden_fields: 4 + template_node_child: true + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 7.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Toggle stencil" + font: "game" + id: "button_toggle_stencil/text" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + shadow { + x: 0.101960786 + y: 0.2 + z: 0.6 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "button_toggle_stencil/button" + layer: "text" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 0.0 + shadow_alpha: 0.78 + overridden_fields: 8 + template_node_child: true + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 0.0 + y: -249.0 z: 0.0 w: 1.0 } @@ -11794,7 +11954,7 @@ nodes { nodes { position { x: -150.0 - y: -644.0 + y: -765.0 z: 0.0 w: 1.0 } @@ -11904,7 +12064,7 @@ nodes { nodes { position { x: 150.0 - y: -644.0 + y: -765.0 z: 0.0 w: 1.0 } @@ -12014,7 +12174,7 @@ nodes { nodes { position { x: 0.0 - y: -968.0 + y: -1089.0 z: 0.0 w: 1.0 } @@ -12124,7 +12284,7 @@ nodes { nodes { position { x: 0.0 - y: -1030.0 + y: -1151.0 z: 0.0 w: 1.0 } @@ -12242,7 +12402,7 @@ nodes { nodes { position { x: -150.0 - y: -176.0 + y: -297.0 z: 0.0 w: 1.0 } @@ -12352,7 +12512,7 @@ nodes { nodes { position { x: 150.0 - y: -176.0 + y: -297.0 z: 0.0 w: 1.0 } @@ -12462,7 +12622,7 @@ nodes { nodes { position { x: 150.0 - y: -820.0 + y: -941.0 z: 0.0 w: 1.0 } @@ -12580,7 +12740,7 @@ nodes { nodes { position { x: 151.0 - y: -356.0 + y: -477.0 z: 0.0 w: 1.0 } diff --git a/example/page/infinity_page.lua b/example/page/infinity_page.lua index 085d2f9..80201a0 100644 --- a/example/page/infinity_page.lua +++ b/example/page/infinity_page.lua @@ -103,6 +103,17 @@ local function setup_infinity_list(self) end +local function toggle_stencil(self) + self._is_stencil = not self._is_stencil + local mode = self._is_stencil and gui.CLIPPING_MODE_STENCIL or gui.CLIPPING_MODE_NONE + gui.set_clipping_mode(self.infinity_scroll.view_node, mode) + gui.set_clipping_mode(self.infinity_scroll_hor.view_node, mode) + gui.set_clipping_mode(self.infinity_scroll_3.view_node, mode) + gui.set_clipping_mode(self.infinity_scroll_dynamic.view_node, mode) + gui.set_clipping_mode(self.infinity_scroll_dynamic_hor.view_node, mode) +end + + function M.setup_page(self) self.druid:new_scroll("infinity_page", "infinity_page_content") @@ -133,6 +144,9 @@ function M.setup_page(self) :set_vertical_scroll(false) self.infinity_grid_dynamic_hor = self.druid:new_dynamic_grid("infinity_scroll_content_dynamic_hor") + self._is_stencil = true + self.druid:new_button("button_toggle_stencil/button", toggle_stencil) + setup_infinity_list(self) end From 14a4e4365ed6021ce3ca04e1eafc5d8c612bf584 Mon Sep 17 00:00:00 2001 From: Insality Date: Sun, 29 Nov 2020 21:51:09 +0300 Subject: [PATCH 19/30] Fix infinity list elements render count, little optimize scroll --- druid/base/scroll.lua | 11 ++++++----- druid/extended/infinity_list.lua | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/druid/base/scroll.lua b/druid/base/scroll.lua index c9a3465..bb323b3 100644 --- a/druid/base/scroll.lua +++ b/druid/base/scroll.lua @@ -133,6 +133,8 @@ function Scroll.init(self, view_node, content_node) self.view_node = self:get_node(view_node) self.content_node = self:get_node(content_node) + self.view_size = vmath.mul_per_elem(gui.get_size(self.view_node), gui.get_scale(self.view_node)) + self.position = gui.get_position(self.content_node) self.target_position = vmath.vector3(self.position) self.inertion = vmath.vector3(0) @@ -621,7 +623,6 @@ end function Scroll._update_size(self) local view_border = helper.get_border(self.view_node) - local view_size = vmath.mul_per_elem(gui.get_size(self.view_node), gui.get_scale(self.view_node)) local content_border = helper.get_border(self.content_node) local content_size = vmath.mul_per_elem(gui.get_size(self.content_node), gui.get_scale(self.content_node)) @@ -639,20 +640,20 @@ function Scroll._update_size(self) local stretch_size = self.style.EXTRA_STRETCH_SIZE if self.drag.can_x then - local sign = content_size.x > view_size.x and 1 or -1 + local sign = content_size.x > self.view_size.x and 1 or -1 content_border_extra.x = content_border_extra.x - stretch_size * sign content_border_extra.z = content_border_extra.z + stretch_size * sign end if self.drag.can_y then - local sign = content_size.y > view_size.y and 1 or -1 + local sign = content_size.y > self.view_size.y and 1 or -1 content_border_extra.y = content_border_extra.y + stretch_size * sign content_border_extra.w = content_border_extra.w - stretch_size * sign end if not self.style.SMALL_CONTENT_SCROLL then - self.drag.can_x = content_size.x > view_size.x - self.drag.can_y = content_size.y > view_size.y + self.drag.can_x = content_size.x > self.view_size.x + self.drag.can_y = content_size.y > self.view_size.y end self.available_pos_extra = get_border_vector(view_border - content_border_extra, self._offset) diff --git a/druid/extended/infinity_list.lua b/druid/extended/infinity_list.lua index 0ab82ac..c9974f0 100644 --- a/druid/extended/infinity_list.lua +++ b/druid/extended/infinity_list.lua @@ -152,7 +152,7 @@ function M:_check_elements_from(index, step) is_outside = true -- remove nexts: - local remove_index = index + local remove_index = index + step while self.nodes[remove_index] do self:_remove_at(remove_index) remove_index = remove_index + step From 897d401142632e770487c758dfd03a19c779b867 Mon Sep 17 00:00:00 2001 From: Insality Date: Sun, 29 Nov 2020 22:17:51 +0300 Subject: [PATCH 20/30] Add more annotations, update grid_page example --- druid/base/scroll.lua | 5 +++++ druid/base/static_grid.lua | 4 +++- druid/extended/dynamic_grid.lua | 29 +++++++++++++++------------ druid/helper.lua | 6 ++++-- example/gui/main/main.gui | 6 +++--- example/page/grid_page.lua | 35 ++++++++++++++++++++++++++++++--- 6 files changed, 63 insertions(+), 22 deletions(-) diff --git a/druid/base/scroll.lua b/druid/base/scroll.lua index bb323b3..4c2c7d3 100644 --- a/druid/base/scroll.lua +++ b/druid/base/scroll.lua @@ -267,6 +267,7 @@ end -- It will change content gui node size -- @tparam Scroll self -- @tparam vector3 size The new size for content node +-- @tparam vector3 offset Offset value to set, where content is starts -- @treturn druid.scroll Current scroll instance function Scroll.set_size(self, size, offset) if offset then @@ -361,6 +362,10 @@ function Scroll.set_vertical_scroll(self, state) end +--- Check node if it visible now on scroll +-- @tparam Scroll self +-- @tparma node node The node to check +-- @treturn boolean True, if node in visible scroll area function Scroll.is_node_in_view(self, node) local node_border = helper.get_border(node, gui.get_position(node)) local view_border = helper.get_border(self.view_node, -self.position) diff --git a/druid/base/static_grid.lua b/druid/base/static_grid.lua index 7dd090f..7906588 100644 --- a/druid/base/static_grid.lua +++ b/druid/base/static_grid.lua @@ -318,7 +318,9 @@ function StaticGrid.clear(self) end --- return vector where content borders starts +--- Return StaticGrid offset, where StaticGrid content starts. +-- @tparam StaticGrid self The StaticGrid instance +-- @treturn vector3 The StaticGrid offset function StaticGrid:get_offset() local borders = self:get_borders() local size = self:get_size() diff --git a/druid/extended/dynamic_grid.lua b/druid/extended/dynamic_grid.lua index e4bcb7f..e8b4413 100644 --- a/druid/extended/dynamic_grid.lua +++ b/druid/extended/dynamic_grid.lua @@ -224,6 +224,21 @@ function DynamicGrid.get_size(self, border) end +--- Return DynamicGrid offset, where DynamicGrid content starts. +-- @tparam DynamicGrid self The DynamicGrid instance +-- @treturn vector3 The DynamicGrid offset +function DynamicGrid.get_offset(self) + local size = self:get_size() + local borders = self:get_borders() + local offset = vmath.vector3( + (borders.z + borders.x)/2 + size.x * self.pivot.x, + (borders.y + borders.w)/2 + size.y * self.pivot.y, + 0) + + return offset +end + + --- Return grid content borders -- @tparam DynamicGrid self -- @treturn vector3 The grid content borders @@ -391,24 +406,12 @@ function DynamicGrid._get_next_node_pos(self, origin_node_index, new_node, place end + function DynamicGrid._get_node_size(self, node) return vmath.mul_per_elem(gui.get_size(node), gui.get_scale(node)) end -function DynamicGrid:get_offset() - -- return vector where content borders starts - local size = self:get_size() - local borders = self:get_borders() - local offset = vmath.vector3( - (borders.z + borders.x)/2 + size.x * self.pivot.x, - (borders.y + borders.w)/2 + size.y * self.pivot.y, - 0) - - return offset -end - - --- Return side vector to correct node shifting function DynamicGrid._get_side_vector(self, side, is_forward) if side == const.SIDE.X then diff --git a/druid/helper.lua b/druid/helper.lua index 4cf245a..fb27ef6 100644 --- a/druid/helper.lua +++ b/druid/helper.lua @@ -183,9 +183,11 @@ function M.is_web() end ---- Distance from node to size border +--- Distance from node position to his borders -- @function helper.get_border --- @return vector4 (left, top, right, down) +-- @tparam node node The gui node to check +-- @tparam vector3 offset The offset to add to result +-- @return vector4 Vector with distance to node border: (left, top, right, down) function M.get_border(node, offset) local pivot = gui.get_pivot(node) local pivot_offset = M.get_pivot_offset(pivot) diff --git a/example/gui/main/main.gui b/example/gui/main/main.gui index b8892d0..0ddc821 100644 --- a/example/gui/main/main.gui +++ b/example/gui/main/main.gui @@ -10614,9 +10614,9 @@ nodes { w: 1.0 } color { - x: 0.6 - y: 0.3019608 - z: 0.4 + x: 1.0 + y: 1.0 + z: 1.0 w: 1.0 } type: TYPE_BOX diff --git a/example/page/grid_page.lua b/example/page/grid_page.lua index 1bac0a2..534b8ba 100644 --- a/example/page/grid_page.lua +++ b/example/page/grid_page.lua @@ -91,6 +91,7 @@ end local function add_node_dynamic(self, index, is_shift_left) local node = gui.clone(self.prefab_dynamic) + gui.set_color(node, vmath.vector4(math.random() * 0.2 + 0.8)) gui.set_enabled(node, true) gui.set_size(node, vmath.vector3(250, math.random(60, 150), 0)) self.grid_dynamic_grid:add(node, index, is_shift_left) @@ -99,24 +100,51 @@ local function add_node_dynamic(self, index, is_shift_left) remove_dynamic_node(self, button) end) button.on_long_click:subscribe(function() - remove_dynamic_node(self, button, const.SHIFT.lEFT) + remove_dynamic_node(self, button, const.SHIFT.LEFT) end) button:set_click_zone(self.grid_dynamic_scroll.view_node) table.insert(self.dynamic_node_buttons, button) end +local function remove_dynamic_hor_node(self, button, shift_policy) + gui.delete_node(button.node) + + self.druid:remove(button) + local index = self.grid_dynamic_hor_grid:get_index_by_node(button.node) + self.grid_dynamic_hor_grid:remove(index, shift_policy) + for i = 1, #self.dynamic_node_hor_buttons do + if self.dynamic_node_hor_buttons[i] == button then + table.remove(self.dynamic_node_hor_buttons, i) + break + end + end +end + + local function add_node_dynamic_hor(self, index) local node = gui.clone(self.prefab_hor_dynamic) + gui.set_color(node, vmath.vector4(math.random() * 0.2 + 0.8)) gui.set_enabled(node, true) gui.set_size(node, vmath.vector3(80 + math.random(0, 80), 80, 0)) + + local button = self.druid:new_button(node, function(_, params, button) + remove_dynamic_hor_node(self, button) + end) + button.on_long_click:subscribe(function() + remove_dynamic_hor_node(self, button, const.SHIFT.LEFT) + end) + button:set_click_zone(self.grid_dynamic_hor_scroll.view_node) + self.grid_dynamic_hor_grid:add(node, index) + table.insert(self.dynamic_node_hor_buttons, button) end local function init_dynamic_grid(self) -- Vertical horizontal grid self.dynamic_node_buttons = {} + self.dynamic_node_hor_buttons = {} self.prefab_dynamic = gui.get_node("grid_dynamic_prefab") gui.set_enabled(self.prefab_dynamic, false) @@ -125,7 +153,7 @@ local function init_dynamic_grid(self) add_node_dynamic(self, i) end self.druid:new_button("button_add_start_dynamic/button", function() - local start_index = (self.grid_dynamic_grid.first_index or 2) - 1 + local start_index = self.grid_dynamic_grid.first_index or 1 add_node_dynamic(self, start_index) end) self.druid:new_button("button_add_end_dynamic/button", function() @@ -141,7 +169,8 @@ local function init_dynamic_grid(self) end self.druid:new_button("button_add_start_dynamic_hor/button", function() - add_node_dynamic_hor(self, 1) + local start_index = self.grid_dynamic_hor_grid.first_index or 1 + add_node_dynamic_hor(self, start_index) end) self.druid:new_button("button_add_end_dynamic_hor/button", function() add_node_dynamic_hor(self) From 2b5c5bf6fac294cb506f5e42f63716db97a1e9bd Mon Sep 17 00:00:00 2001 From: Insality Date: Sun, 29 Nov 2020 23:01:12 +0300 Subject: [PATCH 21/30] Infinity scroll progress --- druid/extended/infinity_list.lua | 117 +++++++++++++++++++++++-------- example/page/infinity_page.lua | 40 ++++++++--- 2 files changed, 117 insertions(+), 40 deletions(-) diff --git a/druid/extended/infinity_list.lua b/druid/extended/infinity_list.lua index c9974f0..e7e8e87 100644 --- a/druid/extended/infinity_list.lua +++ b/druid/extended/infinity_list.lua @@ -15,17 +15,22 @@ function M:init(data_list, scroll, grid, create_function) self.grid = grid self.scroll:bind_grid(grid) - self.data = data_list + --- Current visual elements indexes self.top_index = 1 self.last_index = 1 + self._data = {} + self._data_first_index = false + self._data_last_index = false + self._data_length = 0 + self.create_function = create_function - self.nodes = {} - self.components = {} + self._data_visual = {} - self:_refresh() self.scroll.on_scroll:subscribe(self._check_elements, self) + + self:set_data(data_list) end @@ -35,41 +40,72 @@ end function M:set_data(data_list) - self.data = data_list + self._data = data_list + self:_update_data_info() self:_refresh() end -function M:add(data, index) - table.insert(self.data, index, data) - self:_refresh() +function M:add(data, index, shift_policy) + index = index or self._data_last_index + 1 + shift_policy = shift_policy or const.SHIFT.RIGHT + + if self._data[index] then + if shift_policy == const.SHIFT.RIGHT then + for i = self._data_last_index, index, -1 do + self._data[i + 1] = self._data[i] + end + end + if shift_policy == const.SHIFT.LEFT then + for i = self._data_first_index, index do + self._data[i - 1] = self._data[i] + end + end + end + self._data[index] = data + self:_update_data_info() + self:_check_elements() end function M:remove(index, shift_policy) - table.remove(self.data, index) + table.remove(self._data, index) self:_refresh() end +function M:remove_by_data(data, shift_policy) + local index = helper.contains(self._data, data) + if index then + table.remove(self._data, index) + self:_refresh() + end +end + + function M:clear() - self.data = {} + self._data = {} self:_refresh() end function M:get_first_index() - return self.top_index + return self._data_first_index end function M:get_last_index() - return self.last_index + return self._data_last_index +end + + +function M:get_length() + return self._data_length end function M:get_index(data) - for index, value in pairs(self.data) do + for index, value in pairs(self._data) do if value == data then return index end @@ -79,40 +115,41 @@ end function M:scroll_to_index(index) - self.top_index = helper.clamp(index, 1, #self.data) + self.top_index = helper.clamp(index, 1, #self._data) self:_refresh() self.scroll.on_scroll:trigger(self:get_context(), self) end function M:_add_at(index) - if self.nodes[index] then + if self._data_visual[index] then self:_remove_at(index) end - local node, instance = self.create_function(self.data[index], index) + local node, instance = self.create_function(self._data[index], index) self.grid:add(node, index, const.SHIFT.NO_SHIFT) - self.nodes[index] = node - self.components[index] = instance + self._data_visual[index] = { + node = node, + component = instance + } end function M:_remove_at(index) self.grid:remove(index, const.SHIFT.NO_SHIFT) - local node = self.nodes[index] + local node = self._data_visual[index].node gui.delete_node(node) - self.nodes[index] = nil - if self.components[index] then - self.druid:remove(self.components[index]) - self.components[index] = nil + if self._data_visual[index].component then + self.druid:remove(self._data_visual[index].component) end + self._data_visual[index] = nil end function M:_refresh() - for index, _ in pairs(self.nodes) do + for index, _ in pairs(self._data_visual) do self:_remove_at(index) end self:_check_elements() @@ -120,8 +157,8 @@ end function M:_check_elements() - for index, node in pairs(self.nodes) do - if self.scroll:is_node_in_view(node) then + for index, data in pairs(self._data_visual) do + if self.scroll:is_node_in_view(data.node) then self.top_index = index self.last_index = index end @@ -130,7 +167,7 @@ function M:_check_elements() self:_check_elements_from(self.top_index - 1, -1) self:_check_elements_from(self.top_index, 1) - for index, node in pairs(self.nodes) do + for index, data in pairs(self._data_visual) do self.top_index = math.min(self.top_index or index, index) self.last_index = math.max(self.last_index or index, index) end @@ -140,20 +177,20 @@ end function M:_check_elements_from(index, step) local is_outside = false while not is_outside do - if not self.data[index] then + if not self._data[index] then break end - if not self.nodes[index] then + if not self._data_visual[index] then self:_add_at(index) end - if not self.scroll:is_node_in_view(self.nodes[index]) then + if not self.scroll:is_node_in_view(self._data_visual[index].node) then is_outside = true -- remove nexts: local remove_index = index + step - while self.nodes[remove_index] do + while self._data_visual[remove_index] do self:_remove_at(remove_index) remove_index = remove_index + step end @@ -164,4 +201,22 @@ function M:_check_elements_from(index, step) end +function M:_update_data_info() + self._data_first_index = false + self._data_last_index = false + self._data_length = 0 + + for index, data in pairs(self._data) do + self._data_first_index = math.min(self._data_first_index or index, index) + self._data_last_index = math.max(self._data_last_index or index, index) + self._data_length = self._data_length + 1 + end + + if self._data_length == 0 then + self._data_first_index = 1 + self._data_last_index = 1 + end +end + + return M diff --git a/example/page/infinity_page.lua b/example/page/infinity_page.lua index 80201a0..7988f88 100644 --- a/example/page/infinity_page.lua +++ b/example/page/infinity_page.lua @@ -4,23 +4,43 @@ local function create_infinity_instance(self, record, index) local instance = gui.clone_tree(self.infinity_prefab) gui.set_enabled(instance["infinity_prefab"], true) - gui.set_text(instance["infinity_text"], "Record " .. index) + gui.set_text(instance["infinity_text"], "Record " .. record) local button = self.druid:new_button(instance["infinity_prefab"], function() - print("Infinity click on", index) + print("Infinity click on", record) + self.infinity_list:add(self.infinity_list:get_length() + 1) + end) + button.on_long_click:subscribe(function() + self.infinity_list:remove_by_data(record) end) return instance["infinity_prefab"], button end +local function create_infinity_instance_hor(self, record, index) + local instance = gui.clone_tree(self.infinity_prefab) + gui.set_enabled(instance["infinity_prefab"], true) + gui.set_text(instance["infinity_text"], "Record " .. record) + + local button = self.druid:new_button(instance["infinity_prefab"], function() + print("Infinity click on", record) + self.infinity_list_hor:remove_by_data(record) + end) + + return instance["infinity_prefab"], button +end + + + local function create_infinity_instance_small(self, record, index) local instance = gui.clone_tree(self.infinity_prefab_small) gui.set_enabled(instance["infinity_prefab_small"], true) - gui.set_text(instance["infinity_text_3"], index) + gui.set_text(instance["infinity_text_3"], record) local button = self.druid:new_button(instance["infinity_prefab_small"], function() - print("Infinity click on", index) + print("Infinity click on", record) + self.infinity_list_small:remove_by_data(record) end) button:set_click_zone(self.infinity_scroll_3.view_node) @@ -31,11 +51,12 @@ end local function create_infinity_instance_dynamic(self, record, index) local instance = gui.clone_tree(self.infinity_prefab_dynamic) gui.set_enabled(instance["infinity_prefab_dynamic"], true) - gui.set_text(instance["infinity_text_dynamic"], "Record " .. index) + gui.set_text(instance["infinity_text_dynamic"], "Record " .. record) gui.set_size(instance["infinity_prefab_dynamic"], vmath.vector3(200, 60 + index * 3, 0)) local button = self.druid:new_button(instance["infinity_prefab_dynamic"], function() - print("Dynamic click on", index) + print("Dynamic click on", record) + self.infinity_list_dynamic:remove_by_data(record) end) button:set_click_zone(self.infinity_scroll_dynamic.view_node) @@ -46,11 +67,12 @@ end local function create_infinity_instance_dynamic_hor(self, record, index) local instance = gui.clone_tree(self.infinity_prefab_dynamic) gui.set_enabled(instance["infinity_prefab_dynamic"], true) - gui.set_text(instance["infinity_text_dynamic"], "Record " .. index) + gui.set_text(instance["infinity_text_dynamic"], "Record " .. record) gui.set_size(instance["infinity_prefab_dynamic"], vmath.vector3(150 + 2 * index, 60, 0)) local button = self.druid:new_button(instance["infinity_prefab_dynamic"], function() - print("Dynamic click on", index) + print("Dynamic click on", record) + self.infinity_list_dynamic_hor:remove_by_data(record) end) button:set_click_zone(self.infinity_scroll_dynamic_hor.view_node) @@ -73,7 +95,7 @@ local function setup_infinity_list(self) self.infinity_list_hor = self.druid:new_infinity_list(data, self.infinity_scroll_hor, self.infinity_grid_hor, function(record, index) -- function should return gui_node, [druid_component] - local root, button = create_infinity_instance(self, record, index) + local root, button = create_infinity_instance_hor(self, record, index) button:set_click_zone(self.infinity_scroll_hor.view_node) return root, button end) From 7ac6c9b02b2bc244ef96330e1e69aa678ececc1b Mon Sep 17 00:00:00 2001 From: Insality Date: Sat, 30 Jan 2021 20:16:54 +0500 Subject: [PATCH 22/30] Add outside scroll vector, add correct check for is in view zone --- druid/base/scroll.lua | 33 ++++++++++++++++++++++++++++++-- druid/extended/infinity_list.lua | 2 ++ example/page/infinity_page.lua | 10 +++++----- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/druid/base/scroll.lua b/druid/base/scroll.lua index 4c2c7d3..8f5cac9 100644 --- a/druid/base/scroll.lua +++ b/druid/base/scroll.lua @@ -155,6 +155,7 @@ function Scroll.init(self, view_node, content_node) self._is_vertical_scroll = true self._grid_on_change = nil self._grid_on_change_callback = nil + self._outside_offset_vector = vmath.vector3(0) self:_update_size() end @@ -166,6 +167,7 @@ end function Scroll.update(self, dt) + self:_update_params(dt) if self.drag.is_drag then self:_update_hand_scroll(dt) else @@ -362,13 +364,14 @@ function Scroll.set_vertical_scroll(self, state) end ---- Check node if it visible now on scroll +--- Check node if it visible now on scroll. +-- Extra border is not affected. Return true for elements in extra scroll zone -- @tparam Scroll self -- @tparma node node The node to check -- @treturn boolean True, if node in visible scroll area function Scroll.is_node_in_view(self, node) local node_border = helper.get_border(node, gui.get_position(node)) - local view_border = helper.get_border(self.view_node, -self.position) + local view_border = helper.get_border(self.view_node, -(self.position - self._outside_offset_vector)) -- Check is vertical outside (Left or Right): if node_border.z < view_border.x or node_border.x > view_border.z then @@ -666,4 +669,30 @@ function Scroll._update_size(self) end +function Scroll._update_params(self, dt) + local t = self.target_position + local b = self.available_pos + + self._outside_offset_vector.x = 0 + self._outside_offset_vector.y = 0 + + -- Right border (minimum x) + if t.x < b.x then + self._outside_offset_vector.x = t.x - b.x + end + -- Left border (maximum x) + if t.x > b.z then + self._outside_offset_vector.x = t.x - b.z + end + -- Top border (minimum y) + if t.y < b.y then + self._outside_offset_vector.y = t.y - b.y + end + -- Bot border (maximum y) + if t.y > b.w then + self._outside_offset_vector.y = t.y - b.w + end +end + + return Scroll diff --git a/druid/extended/infinity_list.lua b/druid/extended/infinity_list.lua index e7e8e87..d439820 100644 --- a/druid/extended/infinity_list.lua +++ b/druid/extended/infinity_list.lua @@ -189,6 +189,8 @@ function M:_check_elements_from(index, step) is_outside = true -- remove nexts: + -- We add one more element, which is not in view to + -- check what it's always outside to stop spawning local remove_index = index + step while self._data_visual[remove_index] do self:_remove_at(remove_index) diff --git a/example/page/infinity_page.lua b/example/page/infinity_page.lua index 7988f88..b2f305e 100644 --- a/example/page/infinity_page.lua +++ b/example/page/infinity_page.lua @@ -11,7 +11,7 @@ local function create_infinity_instance(self, record, index) self.infinity_list:add(self.infinity_list:get_length() + 1) end) button.on_long_click:subscribe(function() - self.infinity_list:remove_by_data(record) + -- self.infinity_list:remove_by_data(record) end) return instance["infinity_prefab"], button @@ -25,7 +25,7 @@ local function create_infinity_instance_hor(self, record, index) local button = self.druid:new_button(instance["infinity_prefab"], function() print("Infinity click on", record) - self.infinity_list_hor:remove_by_data(record) + -- self.infinity_list_hor:remove_by_data(record) end) return instance["infinity_prefab"], button @@ -40,7 +40,7 @@ local function create_infinity_instance_small(self, record, index) local button = self.druid:new_button(instance["infinity_prefab_small"], function() print("Infinity click on", record) - self.infinity_list_small:remove_by_data(record) + -- self.infinity_list_small:remove_by_data(record) end) button:set_click_zone(self.infinity_scroll_3.view_node) @@ -56,7 +56,7 @@ local function create_infinity_instance_dynamic(self, record, index) gui.set_size(instance["infinity_prefab_dynamic"], vmath.vector3(200, 60 + index * 3, 0)) local button = self.druid:new_button(instance["infinity_prefab_dynamic"], function() print("Dynamic click on", record) - self.infinity_list_dynamic:remove_by_data(record) + -- self.infinity_list_dynamic:remove_by_data(record) end) button:set_click_zone(self.infinity_scroll_dynamic.view_node) @@ -72,7 +72,7 @@ local function create_infinity_instance_dynamic_hor(self, record, index) gui.set_size(instance["infinity_prefab_dynamic"], vmath.vector3(150 + 2 * index, 60, 0)) local button = self.druid:new_button(instance["infinity_prefab_dynamic"], function() print("Dynamic click on", record) - self.infinity_list_dynamic_hor:remove_by_data(record) + -- self.infinity_list_dynamic_hor:remove_by_data(record) end) button:set_click_zone(self.infinity_scroll_dynamic_hor.view_node) From 8a173305ded2678619676995cd6e8fa01e2c1329 Mon Sep 17 00:00:00 2001 From: Insality Date: Sun, 7 Feb 2021 13:14:30 +0500 Subject: [PATCH 23/30] Better on end touch events --- druid/base/drag.lua | 6 ++++-- druid/base/scroll.lua | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/druid/base/drag.lua b/druid/base/drag.lua index 2eef901..7625c29 100644 --- a/druid/base/drag.lua +++ b/druid/base/drag.lua @@ -71,8 +71,10 @@ local function end_touch(self) end self.is_drag = false - self.is_touch = false - self.on_touch_end:trigger(self:get_context()) + if self.is_touch then + self.is_touch = false + self.on_touch_end:trigger(self:get_context()) + end self:reset_input_priority() self.touch_id = 0 end diff --git a/druid/base/scroll.lua b/druid/base/scroll.lua index 8f5cac9..a01b904 100644 --- a/druid/base/scroll.lua +++ b/druid/base/scroll.lua @@ -569,11 +569,11 @@ end function Scroll._check_threshold(self) local is_stopped = false - if self.inertion.x ~= 0 and math.abs(self.inertion.x) < self.style.INERT_THRESHOLD then + if math.abs(self.inertion.x) < self.style.INERT_THRESHOLD then is_stopped = true self.inertion.x = 0 end - if self.inertion.y ~= 0 and math.abs(self.inertion.y) < self.style.INERT_THRESHOLD then + if math.abs(self.inertion.y) < self.style.INERT_THRESHOLD then is_stopped = true self.inertion.y = 0 end From 6a44a7dcff917b7145f12379906e1825d6b52e18 Mon Sep 17 00:00:00 2001 From: Insality Date: Thu, 1 Apr 2021 21:29:28 +0300 Subject: [PATCH 24/30] Replace spaces with tabs --- druid/extended/infinity_list.lua | 249 ++++++++++++++++--------------- example/page/infinity_page.lua | 230 ++++++++++++++-------------- 2 files changed, 240 insertions(+), 239 deletions(-) diff --git a/druid/extended/infinity_list.lua b/druid/extended/infinity_list.lua index d439820..9a5ac25 100644 --- a/druid/extended/infinity_list.lua +++ b/druid/extended/infinity_list.lua @@ -8,216 +8,217 @@ local M = component.create("infinity_list") function M:init(data_list, scroll, grid, create_function) - self.view_size = gui.get_size(scroll.view_node) - self.prefab_size = grid.node_size - self.druid = self:get_druid() - self.scroll = scroll - self.grid = grid - self.scroll:bind_grid(grid) + self.view_size = gui.get_size(scroll.view_node) + self.prefab_size = grid.node_size + self.druid = self:get_druid() + self.scroll = scroll + self.grid = grid + self.scroll:bind_grid(grid) - --- Current visual elements indexes - self.top_index = 1 - self.last_index = 1 + --- Current visual elements indexes + self.top_index = 1 + self.last_index = 1 - self._data = {} - self._data_first_index = false - self._data_last_index = false - self._data_length = 0 + self._data = {} + self._data_first_index = false + self._data_last_index = false + self._data_length = 0 - self.create_function = create_function + self.create_function = create_function - self._data_visual = {} + self._data_visual = {} - self.scroll.on_scroll:subscribe(self._check_elements, self) + self.scroll.on_scroll:subscribe(self._check_elements, self) - self:set_data(data_list) + self:set_data(data_list) end function M:on_remove() - self.scroll.on_scroll:unsubscribe(self._check_elements, self) + self.scroll.on_scroll:unsubscribe(self._check_elements, self) end function M:set_data(data_list) - self._data = data_list - self:_update_data_info() - self:_refresh() + self._data = data_list + self:_update_data_info() + self:_refresh() end function M:add(data, index, shift_policy) - index = index or self._data_last_index + 1 - shift_policy = shift_policy or const.SHIFT.RIGHT + index = index or self._data_last_index + 1 + shift_policy = shift_policy or const.SHIFT.RIGHT - if self._data[index] then - if shift_policy == const.SHIFT.RIGHT then - for i = self._data_last_index, index, -1 do - self._data[i + 1] = self._data[i] - end - end - if shift_policy == const.SHIFT.LEFT then - for i = self._data_first_index, index do - self._data[i - 1] = self._data[i] - end - end - end - self._data[index] = data - self:_update_data_info() - self:_check_elements() + if self._data[index] then + if shift_policy == const.SHIFT.RIGHT then + for i = self._data_last_index, index, -1 do + self._data[i + 1] = self._data[i] + end + end + if shift_policy == const.SHIFT.LEFT then + for i = self._data_first_index, index do + self._data[i - 1] = self._data[i] + end + end + end + self._data[index] = data + self:_update_data_info() + self:_check_elements() end function M:remove(index, shift_policy) - table.remove(self._data, index) - self:_refresh() + table.remove(self._data, index) + self:_refresh() end function M:remove_by_data(data, shift_policy) - local index = helper.contains(self._data, data) - if index then - table.remove(self._data, index) - self:_refresh() - end + local index = helper.contains(self._data, data) + if index then + table.remove(self._data, index) + self:_refresh() + end end function M:clear() - self._data = {} - self:_refresh() + self._data = {} + self:_refresh() end function M:get_first_index() - return self._data_first_index + return self._data_first_index end function M:get_last_index() - return self._data_last_index + return self._data_last_index end function M:get_length() - return self._data_length + return self._data_length end function M:get_index(data) - for index, value in pairs(self._data) do - if value == data then - return index - end - end - return nil + for index, value in pairs(self._data) do + if value == data then + return index + end + end + + return nil end function M:scroll_to_index(index) - self.top_index = helper.clamp(index, 1, #self._data) - self:_refresh() - self.scroll.on_scroll:trigger(self:get_context(), self) + self.top_index = helper.clamp(index, 1, #self._data) + self:_refresh() + self.scroll.on_scroll:trigger(self:get_context(), self) end function M:_add_at(index) - if self._data_visual[index] then - self:_remove_at(index) - end + if self._data_visual[index] then + self:_remove_at(index) + end - local node, instance = self.create_function(self._data[index], index) - self.grid:add(node, index, const.SHIFT.NO_SHIFT) - self._data_visual[index] = { - node = node, - component = instance - } + local node, instance = self.create_function(self._data[index], index) + self.grid:add(node, index, const.SHIFT.NO_SHIFT) + self._data_visual[index] = { + node = node, + component = instance + } end function M:_remove_at(index) - self.grid:remove(index, const.SHIFT.NO_SHIFT) + self.grid:remove(index, const.SHIFT.NO_SHIFT) - local node = self._data_visual[index].node - gui.delete_node(node) + local node = self._data_visual[index].node + gui.delete_node(node) - if self._data_visual[index].component then - self.druid:remove(self._data_visual[index].component) - end - self._data_visual[index] = nil + if self._data_visual[index].component then + self.druid:remove(self._data_visual[index].component) + end + self._data_visual[index] = nil end function M:_refresh() - for index, _ in pairs(self._data_visual) do - self:_remove_at(index) - end - self:_check_elements() + for index, _ in pairs(self._data_visual) do + self:_remove_at(index) + end + self:_check_elements() end function M:_check_elements() - for index, data in pairs(self._data_visual) do - if self.scroll:is_node_in_view(data.node) then - self.top_index = index - self.last_index = index - end - end + for index, data in pairs(self._data_visual) do + if self.scroll:is_node_in_view(data.node) then + self.top_index = index + self.last_index = index + end + end - self:_check_elements_from(self.top_index - 1, -1) - self:_check_elements_from(self.top_index, 1) + self:_check_elements_from(self.top_index - 1, -1) + self:_check_elements_from(self.top_index, 1) - for index, data in pairs(self._data_visual) do - self.top_index = math.min(self.top_index or index, index) - self.last_index = math.max(self.last_index or index, index) - end + for index, data in pairs(self._data_visual) do + self.top_index = math.min(self.top_index or index, index) + self.last_index = math.max(self.last_index or index, index) + end end function M:_check_elements_from(index, step) - local is_outside = false - while not is_outside do - if not self._data[index] then - break - end + local is_outside = false + while not is_outside do + if not self._data[index] then + break + end - if not self._data_visual[index] then - self:_add_at(index) - end + if not self._data_visual[index] then + self:_add_at(index) + end - if not self.scroll:is_node_in_view(self._data_visual[index].node) then - is_outside = true + if not self.scroll:is_node_in_view(self._data_visual[index].node) then + is_outside = true - -- remove nexts: - -- We add one more element, which is not in view to - -- check what it's always outside to stop spawning - local remove_index = index + step - while self._data_visual[remove_index] do - self:_remove_at(remove_index) - remove_index = remove_index + step - end - end + -- remove nexts: + -- We add one more element, which is not in view to + -- check what it's always outside to stop spawning + local remove_index = index + step + while self._data_visual[remove_index] do + self:_remove_at(remove_index) + remove_index = remove_index + step + end + end - index = index + step - end + index = index + step + end end function M:_update_data_info() - self._data_first_index = false - self._data_last_index = false - self._data_length = 0 + self._data_first_index = false + self._data_last_index = false + self._data_length = 0 - for index, data in pairs(self._data) do - self._data_first_index = math.min(self._data_first_index or index, index) - self._data_last_index = math.max(self._data_last_index or index, index) - self._data_length = self._data_length + 1 - end + for index, data in pairs(self._data) do + self._data_first_index = math.min(self._data_first_index or index, index) + self._data_last_index = math.max(self._data_last_index or index, index) + self._data_length = self._data_length + 1 + end - if self._data_length == 0 then - self._data_first_index = 1 - self._data_last_index = 1 - end + if self._data_length == 0 then + self._data_first_index = 1 + self._data_last_index = 1 + end end diff --git a/example/page/infinity_page.lua b/example/page/infinity_page.lua index b2f305e..132d951 100644 --- a/example/page/infinity_page.lua +++ b/example/page/infinity_page.lua @@ -1,175 +1,175 @@ - local M = {} + local M = {} local function create_infinity_instance(self, record, index) - local instance = gui.clone_tree(self.infinity_prefab) - gui.set_enabled(instance["infinity_prefab"], true) - gui.set_text(instance["infinity_text"], "Record " .. record) + local instance = gui.clone_tree(self.infinity_prefab) + gui.set_enabled(instance["infinity_prefab"], true) + gui.set_text(instance["infinity_text"], "Record " .. record) - local button = self.druid:new_button(instance["infinity_prefab"], function() - print("Infinity click on", record) - self.infinity_list:add(self.infinity_list:get_length() + 1) - end) - button.on_long_click:subscribe(function() - -- self.infinity_list:remove_by_data(record) - end) + local button = self.druid:new_button(instance["infinity_prefab"], function() + print("Infinity click on", record) + self.infinity_list:add(self.infinity_list:get_length() + 1) + end) + button.on_long_click:subscribe(function() + -- self.infinity_list:remove_by_data(record) + end) - return instance["infinity_prefab"], button + return instance["infinity_prefab"], button end local function create_infinity_instance_hor(self, record, index) - local instance = gui.clone_tree(self.infinity_prefab) - gui.set_enabled(instance["infinity_prefab"], true) - gui.set_text(instance["infinity_text"], "Record " .. record) + local instance = gui.clone_tree(self.infinity_prefab) + gui.set_enabled(instance["infinity_prefab"], true) + gui.set_text(instance["infinity_text"], "Record " .. record) - local button = self.druid:new_button(instance["infinity_prefab"], function() - print("Infinity click on", record) - -- self.infinity_list_hor:remove_by_data(record) - end) + local button = self.druid:new_button(instance["infinity_prefab"], function() + print("Infinity click on", record) + -- self.infinity_list_hor:remove_by_data(record) + end) - return instance["infinity_prefab"], button + return instance["infinity_prefab"], button end local function create_infinity_instance_small(self, record, index) - local instance = gui.clone_tree(self.infinity_prefab_small) - gui.set_enabled(instance["infinity_prefab_small"], true) - gui.set_text(instance["infinity_text_3"], record) + local instance = gui.clone_tree(self.infinity_prefab_small) + gui.set_enabled(instance["infinity_prefab_small"], true) + gui.set_text(instance["infinity_text_3"], record) - local button = self.druid:new_button(instance["infinity_prefab_small"], function() - print("Infinity click on", record) - -- self.infinity_list_small:remove_by_data(record) - end) - button:set_click_zone(self.infinity_scroll_3.view_node) + local button = self.druid:new_button(instance["infinity_prefab_small"], function() + print("Infinity click on", record) + -- self.infinity_list_small:remove_by_data(record) + end) + button:set_click_zone(self.infinity_scroll_3.view_node) - return instance["infinity_prefab_small"], button + return instance["infinity_prefab_small"], button end local function create_infinity_instance_dynamic(self, record, index) - local instance = gui.clone_tree(self.infinity_prefab_dynamic) - gui.set_enabled(instance["infinity_prefab_dynamic"], true) - gui.set_text(instance["infinity_text_dynamic"], "Record " .. record) + local instance = gui.clone_tree(self.infinity_prefab_dynamic) + gui.set_enabled(instance["infinity_prefab_dynamic"], true) + gui.set_text(instance["infinity_text_dynamic"], "Record " .. record) - gui.set_size(instance["infinity_prefab_dynamic"], vmath.vector3(200, 60 + index * 3, 0)) - local button = self.druid:new_button(instance["infinity_prefab_dynamic"], function() - print("Dynamic click on", record) - -- self.infinity_list_dynamic:remove_by_data(record) - end) - button:set_click_zone(self.infinity_scroll_dynamic.view_node) + gui.set_size(instance["infinity_prefab_dynamic"], vmath.vector3(200, 60 + index * 3, 0)) + local button = self.druid:new_button(instance["infinity_prefab_dynamic"], function() + print("Dynamic click on", record) + -- self.infinity_list_dynamic:remove_by_data(record) + end) + button:set_click_zone(self.infinity_scroll_dynamic.view_node) - return instance["infinity_prefab_dynamic"], button + return instance["infinity_prefab_dynamic"], button end local function create_infinity_instance_dynamic_hor(self, record, index) - local instance = gui.clone_tree(self.infinity_prefab_dynamic) - gui.set_enabled(instance["infinity_prefab_dynamic"], true) - gui.set_text(instance["infinity_text_dynamic"], "Record " .. record) + local instance = gui.clone_tree(self.infinity_prefab_dynamic) + gui.set_enabled(instance["infinity_prefab_dynamic"], true) + gui.set_text(instance["infinity_text_dynamic"], "Record " .. record) - gui.set_size(instance["infinity_prefab_dynamic"], vmath.vector3(150 + 2 * index, 60, 0)) - local button = self.druid:new_button(instance["infinity_prefab_dynamic"], function() - print("Dynamic click on", record) - -- self.infinity_list_dynamic_hor:remove_by_data(record) - end) - button:set_click_zone(self.infinity_scroll_dynamic_hor.view_node) + gui.set_size(instance["infinity_prefab_dynamic"], vmath.vector3(150 + 2 * index, 60, 0)) + local button = self.druid:new_button(instance["infinity_prefab_dynamic"], function() + print("Dynamic click on", record) + -- self.infinity_list_dynamic_hor:remove_by_data(record) + end) + button:set_click_zone(self.infinity_scroll_dynamic_hor.view_node) - return instance["infinity_prefab_dynamic"], button + return instance["infinity_prefab_dynamic"], button end local function setup_infinity_list(self) - local data = {} - for i = 1, 50 do - table.insert(data, i) - end + local data = {} + for i = 1, 50 do + table.insert(data, i) + end - self.infinity_list = self.druid:new_infinity_list(data, self.infinity_scroll, self.infinity_grid, function(record, index) - -- function should return gui_node, [druid_component] - local root, button = create_infinity_instance(self, record, index) - button:set_click_zone(self.infinity_scroll.view_node) - return root, button - end) + self.infinity_list = self.druid:new_infinity_list(data, self.infinity_scroll, self.infinity_grid, function(record, index) + -- function should return gui_node, [druid_component] + local root, button = create_infinity_instance(self, record, index) + button:set_click_zone(self.infinity_scroll.view_node) + return root, button + end) - self.infinity_list_hor = self.druid:new_infinity_list(data, self.infinity_scroll_hor, self.infinity_grid_hor, function(record, index) - -- function should return gui_node, [druid_component] - local root, button = create_infinity_instance_hor(self, record, index) - button:set_click_zone(self.infinity_scroll_hor.view_node) - return root, button - end) + self.infinity_list_hor = self.druid:new_infinity_list(data, self.infinity_scroll_hor, self.infinity_grid_hor, function(record, index) + -- function should return gui_node, [druid_component] + local root, button = create_infinity_instance_hor(self, record, index) + button:set_click_zone(self.infinity_scroll_hor.view_node) + return root, button + end) - -- scroll to some index - -- local pos = self.infinity_grid:get_pos(25) - -- self.infinity_scroll:scroll_to(pos, true) - -- timer.delay(1, false, function() - -- self.infinity_list:scroll_to_index(1) - -- end) + -- scroll to some index + -- local pos = self.infinity_grid:get_pos(25) + -- self.infinity_scroll:scroll_to(pos, true) + timer.delay(1, false, function() + self.infinity_list:scroll_to_index(25) + end) - self.infinity_list_small = self.druid:new_infinity_list(data, self.infinity_scroll_3, self.infinity_grid_3, function(record, index) - -- function should return gui_node, [druid_component] - return create_infinity_instance_small(self, record, index) - end) + self.infinity_list_small = self.druid:new_infinity_list(data, self.infinity_scroll_3, self.infinity_grid_3, function(record, index) + -- function should return gui_node, [druid_component] + return create_infinity_instance_small(self, record, index) + end) - self.infinity_list_dynamic = self.druid:new_infinity_list(data, self.infinity_scroll_dynamic, self.infinity_grid_dynamic, function(record, index) - -- function should return gui_node, [druid_component] - return create_infinity_instance_dynamic(self, record, index) - end) + self.infinity_list_dynamic = self.druid:new_infinity_list(data, self.infinity_scroll_dynamic, self.infinity_grid_dynamic, function(record, index) + -- function should return gui_node, [druid_component] + return create_infinity_instance_dynamic(self, record, index) + end) - self.infinity_list_dynamic_hor = self.druid:new_infinity_list(data, self.infinity_scroll_dynamic_hor, self.infinity_grid_dynamic_hor, function(record, index) - -- function should return gui_node, [druid_component] - return create_infinity_instance_dynamic_hor(self, record, index) - end) + self.infinity_list_dynamic_hor = self.druid:new_infinity_list(data, self.infinity_scroll_dynamic_hor, self.infinity_grid_dynamic_hor, function(record, index) + -- function should return gui_node, [druid_component] + return create_infinity_instance_dynamic_hor(self, record, index) + end) end local function toggle_stencil(self) - self._is_stencil = not self._is_stencil - local mode = self._is_stencil and gui.CLIPPING_MODE_STENCIL or gui.CLIPPING_MODE_NONE - gui.set_clipping_mode(self.infinity_scroll.view_node, mode) - gui.set_clipping_mode(self.infinity_scroll_hor.view_node, mode) - gui.set_clipping_mode(self.infinity_scroll_3.view_node, mode) - gui.set_clipping_mode(self.infinity_scroll_dynamic.view_node, mode) - gui.set_clipping_mode(self.infinity_scroll_dynamic_hor.view_node, mode) + self._is_stencil = not self._is_stencil + local mode = self._is_stencil and gui.CLIPPING_MODE_STENCIL or gui.CLIPPING_MODE_NONE + gui.set_clipping_mode(self.infinity_scroll.view_node, mode) + gui.set_clipping_mode(self.infinity_scroll_hor.view_node, mode) + gui.set_clipping_mode(self.infinity_scroll_3.view_node, mode) + gui.set_clipping_mode(self.infinity_scroll_dynamic.view_node, mode) + gui.set_clipping_mode(self.infinity_scroll_dynamic_hor.view_node, mode) end function M.setup_page(self) - self.druid:new_scroll("infinity_page", "infinity_page_content") + self.druid:new_scroll("infinity_page", "infinity_page_content") - self.infinity_prefab = gui.get_node("infinity_prefab") - self.infinity_prefab_small = gui.get_node("infinity_prefab_small") - self.infinity_prefab_dynamic = gui.get_node("infinity_prefab_dynamic") - gui.set_enabled(self.infinity_prefab, false) - gui.set_enabled(self.infinity_prefab_small, false) - gui.set_enabled(self.infinity_prefab_dynamic, false) + self.infinity_prefab = gui.get_node("infinity_prefab") + self.infinity_prefab_small = gui.get_node("infinity_prefab_small") + self.infinity_prefab_dynamic = gui.get_node("infinity_prefab_dynamic") + gui.set_enabled(self.infinity_prefab, false) + gui.set_enabled(self.infinity_prefab_small, false) + gui.set_enabled(self.infinity_prefab_dynamic, false) - self.infinity_scroll = self.druid:new_scroll("infinity_scroll_stencil", "infinity_scroll_content") - :set_horizontal_scroll(false) - self.infinity_grid = self.druid:new_static_grid("infinity_scroll_content", "infinity_prefab", 1) + self.infinity_scroll = self.druid:new_scroll("infinity_scroll_stencil", "infinity_scroll_content") + :set_horizontal_scroll(false) + self.infinity_grid = self.druid:new_static_grid("infinity_scroll_content", "infinity_prefab", 1) - self.infinity_scroll_hor = self.druid:new_scroll("infinity_scroll_stencil_hor", "infinity_scroll_content_hor") - :set_vertical_scroll(false) - self.infinity_grid_hor = self.druid:new_static_grid("infinity_scroll_content_hor", "infinity_prefab", 999) + self.infinity_scroll_hor = self.druid:new_scroll("infinity_scroll_stencil_hor", "infinity_scroll_content_hor") + :set_vertical_scroll(false) + self.infinity_grid_hor = self.druid:new_static_grid("infinity_scroll_content_hor", "infinity_prefab", 999) - self.infinity_scroll_3 = self.druid:new_scroll("infinity_scroll_3_stencil", "infinity_scroll_3_content") - :set_horizontal_scroll(false) - self.infinity_grid_3 = self.druid:new_static_grid("infinity_scroll_3_content", "infinity_prefab_small", 3) + self.infinity_scroll_3 = self.druid:new_scroll("infinity_scroll_3_stencil", "infinity_scroll_3_content") + :set_horizontal_scroll(false) + self.infinity_grid_3 = self.druid:new_static_grid("infinity_scroll_3_content", "infinity_prefab_small", 3) - self.infinity_scroll_dynamic = self.druid:new_scroll("infinity_scroll_stencil_dynamic", "infinity_scroll_content_dynamic") - :set_horizontal_scroll(false) - self.infinity_grid_dynamic = self.druid:new_dynamic_grid("infinity_scroll_content_dynamic") + self.infinity_scroll_dynamic = self.druid:new_scroll("infinity_scroll_stencil_dynamic", "infinity_scroll_content_dynamic") + :set_horizontal_scroll(false) + self.infinity_grid_dynamic = self.druid:new_dynamic_grid("infinity_scroll_content_dynamic") - self.infinity_scroll_dynamic_hor = self.druid:new_scroll("infinity_scroll_stencil_dynamic_hor", "infinity_scroll_content_dynamic_hor") - :set_vertical_scroll(false) - self.infinity_grid_dynamic_hor = self.druid:new_dynamic_grid("infinity_scroll_content_dynamic_hor") + self.infinity_scroll_dynamic_hor = self.druid:new_scroll("infinity_scroll_stencil_dynamic_hor", "infinity_scroll_content_dynamic_hor") + :set_vertical_scroll(false) + self.infinity_grid_dynamic_hor = self.druid:new_dynamic_grid("infinity_scroll_content_dynamic_hor") - self._is_stencil = true - self.druid:new_button("button_toggle_stencil/button", toggle_stencil) + self._is_stencil = true + self.druid:new_button("button_toggle_stencil/button", toggle_stencil) - setup_infinity_list(self) + setup_infinity_list(self) end From 03a00fe3d26261c58c604437e12295414ea87d5c Mon Sep 17 00:00:00 2001 From: Insality Date: Thu, 1 Apr 2021 21:58:46 +0300 Subject: [PATCH 25/30] Update infinity list --- druid/base/static_grid.lua | 1 - druid/extended/infinity_list.lua | 8 +++----- example/page/infinity_page.lua | 4 ++++ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/druid/base/static_grid.lua b/druid/base/static_grid.lua index 6191a53..7d05e37 100644 --- a/druid/base/static_grid.lua +++ b/druid/base/static_grid.lua @@ -184,7 +184,6 @@ function StaticGrid.add(self, item, index, shift_policy) end self.nodes[index] = item - gui.set_parent(item, self.parent) -- Add new item instantly in new pos. Break update function for correct positioning diff --git a/druid/extended/infinity_list.lua b/druid/extended/infinity_list.lua index 9a5ac25..faa0ffb 100644 --- a/druid/extended/infinity_list.lua +++ b/druid/extended/infinity_list.lua @@ -18,14 +18,12 @@ function M:init(data_list, scroll, grid, create_function) --- Current visual elements indexes self.top_index = 1 self.last_index = 1 + self.create_function = create_function self._data = {} self._data_first_index = false self._data_last_index = false self._data_length = 0 - - self.create_function = create_function - self._data_visual = {} self.scroll.on_scroll:subscribe(self._check_elements, self) @@ -165,8 +163,8 @@ function M:_check_elements() end end - self:_check_elements_from(self.top_index - 1, -1) - self:_check_elements_from(self.top_index, 1) + self:_check_elements_from(self.top_index, -1) + self:_check_elements_from(self.top_index + 1, 1) for index, data in pairs(self._data_visual) do self.top_index = math.min(self.top_index or index, index) diff --git a/example/page/infinity_page.lua b/example/page/infinity_page.lua index 132d951..1e44cbd 100644 --- a/example/page/infinity_page.lua +++ b/example/page/infinity_page.lua @@ -118,6 +118,10 @@ local function setup_infinity_list(self) return create_infinity_instance_dynamic(self, record, index) end) + timer.delay(1, false, function() + self.infinity_list_dynamic:scroll_to_index(25) + end) + self.infinity_list_dynamic_hor = self.druid:new_infinity_list(data, self.infinity_scroll_dynamic_hor, self.infinity_grid_dynamic_hor, function(record, index) -- function should return gui_node, [druid_component] return create_infinity_instance_dynamic_hor(self, record, index) From 72efdc13bf84e5e3dc723ee1fa42f79d7461d405 Mon Sep 17 00:00:00 2001 From: Insality Date: Thu, 1 Apr 2021 22:02:15 +0300 Subject: [PATCH 26/30] Rename Infinity list to Data list --- .../{infinity_list.lua => data_list.lua} | 44 +++++++++---------- druid/system/druid_instance.lua | 12 ++--- example/page/infinity_page.lua | 10 ++--- 3 files changed, 33 insertions(+), 33 deletions(-) rename druid/extended/{infinity_list.lua => data_list.lua} (81%) diff --git a/druid/extended/infinity_list.lua b/druid/extended/data_list.lua similarity index 81% rename from druid/extended/infinity_list.lua rename to druid/extended/data_list.lua index faa0ffb..7b51566 100644 --- a/druid/extended/infinity_list.lua +++ b/druid/extended/data_list.lua @@ -4,10 +4,10 @@ local const = require("druid.const") local helper = require("druid.helper") local component = require("druid.component") -local M = component.create("infinity_list") +local DataList = component.create("data_list") -function M:init(data_list, scroll, grid, create_function) +function DataList.init(self, data, scroll, grid, create_function) self.view_size = gui.get_size(scroll.view_node) self.prefab_size = grid.node_size self.druid = self:get_druid() @@ -28,23 +28,23 @@ function M:init(data_list, scroll, grid, create_function) self.scroll.on_scroll:subscribe(self._check_elements, self) - self:set_data(data_list) + self:set_data(data) end -function M:on_remove() +function DataList.on_remove(self) self.scroll.on_scroll:unsubscribe(self._check_elements, self) end -function M:set_data(data_list) - self._data = data_list +function DataList.set_data(self, data) + self._data = data self:_update_data_info() self:_refresh() end -function M:add(data, index, shift_policy) +function DataList.add(self, data, index, shift_policy) index = index or self._data_last_index + 1 shift_policy = shift_policy or const.SHIFT.RIGHT @@ -66,13 +66,13 @@ function M:add(data, index, shift_policy) end -function M:remove(index, shift_policy) +function DataList.remove(self, index, shift_policy) table.remove(self._data, index) self:_refresh() end -function M:remove_by_data(data, shift_policy) +function DataList.remove_by_data(self, data, shift_policy) local index = helper.contains(self._data, data) if index then table.remove(self._data, index) @@ -81,28 +81,28 @@ function M:remove_by_data(data, shift_policy) end -function M:clear() +function DataList.clear(self) self._data = {} self:_refresh() end -function M:get_first_index() +function DataList.get_first_index(self) return self._data_first_index end -function M:get_last_index() +function DataList.get_last_index(self) return self._data_last_index end -function M:get_length() +function DataList.get_length(self) return self._data_length end -function M:get_index(data) +function DataList.get_index(self, data) for index, value in pairs(self._data) do if value == data then return index @@ -113,14 +113,14 @@ function M:get_index(data) end -function M:scroll_to_index(index) +function DataList.scroll_to_index(self, index) self.top_index = helper.clamp(index, 1, #self._data) self:_refresh() self.scroll.on_scroll:trigger(self:get_context(), self) end -function M:_add_at(index) +function DataList._add_at(self, index) if self._data_visual[index] then self:_remove_at(index) end @@ -134,7 +134,7 @@ function M:_add_at(index) end -function M:_remove_at(index) +function DataList._remove_at(self, index) self.grid:remove(index, const.SHIFT.NO_SHIFT) local node = self._data_visual[index].node @@ -147,7 +147,7 @@ function M:_remove_at(index) end -function M:_refresh() +function DataList._refresh(self) for index, _ in pairs(self._data_visual) do self:_remove_at(index) end @@ -155,7 +155,7 @@ function M:_refresh() end -function M:_check_elements() +function DataList._check_elements(self) for index, data in pairs(self._data_visual) do if self.scroll:is_node_in_view(data.node) then self.top_index = index @@ -173,7 +173,7 @@ function M:_check_elements() end -function M:_check_elements_from(index, step) +function DataList._check_elements_from(self, index, step) local is_outside = false while not is_outside do if not self._data[index] then @@ -202,7 +202,7 @@ function M:_check_elements_from(index, step) end -function M:_update_data_info() +function DataList._update_data_info(self) self._data_first_index = false self._data_last_index = false self._data_length = 0 @@ -220,4 +220,4 @@ function M:_update_data_info() end -return M +return DataList diff --git a/druid/system/druid_instance.lua b/druid/system/druid_instance.lua index 7a913d9..62149c2 100644 --- a/druid/system/druid_instance.lua +++ b/druid/system/druid_instance.lua @@ -53,7 +53,7 @@ local progress = require("druid.extended.progress") local radio_group = require("druid.extended.radio_group") local slider = require("druid.extended.slider") local timer = require("druid.extended.timer") -local infinity_list = require("druid.extended.infinity_list") +local data_list = require("druid.extended.data_list") local DruidInstance = class("druid.druid_instance") @@ -542,12 +542,12 @@ function DruidInstance.new_checkbox_group(self, nodes, callback, click_nodes) end ---- Create infinity list basic component --- @function druid:new_infinity_list +--- Create data list basic component +-- @function druid:new_data_list -- @tparam args ... drag init args --- @treturn Component infinity list component -function DruidInstance.new_infinity_list(self, ...) - return DruidInstance.create(self, infinity_list, ...) +-- @treturn Component data list component +function DruidInstance.new_data_list(self, ...) + return DruidInstance.create(self, data_list, ...) end diff --git a/example/page/infinity_page.lua b/example/page/infinity_page.lua index 1e44cbd..514189e 100644 --- a/example/page/infinity_page.lua +++ b/example/page/infinity_page.lua @@ -86,14 +86,14 @@ local function setup_infinity_list(self) table.insert(data, i) end - self.infinity_list = self.druid:new_infinity_list(data, self.infinity_scroll, self.infinity_grid, function(record, index) + self.infinity_list = self.druid:new_data_list(data, self.infinity_scroll, self.infinity_grid, function(record, index) -- function should return gui_node, [druid_component] local root, button = create_infinity_instance(self, record, index) button:set_click_zone(self.infinity_scroll.view_node) return root, button end) - self.infinity_list_hor = self.druid:new_infinity_list(data, self.infinity_scroll_hor, self.infinity_grid_hor, function(record, index) + self.infinity_list_hor = self.druid:new_data_list(data, self.infinity_scroll_hor, self.infinity_grid_hor, function(record, index) -- function should return gui_node, [druid_component] local root, button = create_infinity_instance_hor(self, record, index) button:set_click_zone(self.infinity_scroll_hor.view_node) @@ -108,12 +108,12 @@ local function setup_infinity_list(self) end) - self.infinity_list_small = self.druid:new_infinity_list(data, self.infinity_scroll_3, self.infinity_grid_3, function(record, index) + self.infinity_list_small = self.druid:new_data_list(data, self.infinity_scroll_3, self.infinity_grid_3, function(record, index) -- function should return gui_node, [druid_component] return create_infinity_instance_small(self, record, index) end) - self.infinity_list_dynamic = self.druid:new_infinity_list(data, self.infinity_scroll_dynamic, self.infinity_grid_dynamic, function(record, index) + self.infinity_list_dynamic = self.druid:new_data_list(data, self.infinity_scroll_dynamic, self.infinity_grid_dynamic, function(record, index) -- function should return gui_node, [druid_component] return create_infinity_instance_dynamic(self, record, index) end) @@ -122,7 +122,7 @@ local function setup_infinity_list(self) self.infinity_list_dynamic:scroll_to_index(25) end) - self.infinity_list_dynamic_hor = self.druid:new_infinity_list(data, self.infinity_scroll_dynamic_hor, self.infinity_grid_dynamic_hor, function(record, index) + self.infinity_list_dynamic_hor = self.druid:new_data_list(data, self.infinity_scroll_dynamic_hor, self.infinity_grid_dynamic_hor, function(record, index) -- function should return gui_node, [druid_component] return create_infinity_instance_dynamic_hor(self, record, index) end) From 2917affe5ecc9a713e2cc21387f8be35b2aa0f44 Mon Sep 17 00:00:00 2001 From: Insality Date: Thu, 1 Apr 2021 22:47:12 +0300 Subject: [PATCH 27/30] #123 Add mouse scroll for Scroll component --- docs_md/changelog.md | 5 +++++ druid/base/hover.lua | 3 +++ druid/base/scroll.lua | 45 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/docs_md/changelog.md b/docs_md/changelog.md index 4fc0160..f4f2d46 100644 --- a/docs_md/changelog.md +++ b/docs_md/changelog.md @@ -152,3 +152,8 @@ _after:_ ```lua local Drag = component.create("drag", { component.ON_INPUT }, const.PRIORITY_INPUT_HIGH) ``` +- **#123** Add scroll for Scroll component via mouse wheel or touchpad: +-- Added Scroll style params: WHEEL_SCROLL_SPEED, WHEEL_SCROLL_INVERTED +-- Mouse scroll working when cursor is hover on scroll view node +-- Vertical scroll have more priority than horizontal +-- Fix: When Hover component node became disabled, reset hover state (throw on_hover and on_mouse_hover events) diff --git a/druid/base/hover.lua b/druid/base/hover.lua index b426f58..1fe9ddf 100644 --- a/druid/base/hover.lua +++ b/druid/base/hover.lua @@ -40,11 +40,14 @@ function Hover.on_input(self, action_id, action) return false end + -- Disable nil (it's mouse) hover or mobile platforms if not action_id and helper.is_mobile() then return false end if not helper.is_enabled(self.node) or not self._is_enabled then + self:set_hover(false) + self:set_mouse_hover(false) return false end diff --git a/druid/base/scroll.lua b/druid/base/scroll.lua index fa41434..5dcf2c8 100644 --- a/druid/base/scroll.lua +++ b/druid/base/scroll.lua @@ -54,10 +54,11 @@ local Event = require("druid.event") +local const = require("druid.const") local helper = require("druid.helper") local component = require("druid.component") -local Scroll = component.create("scroll", { component.ON_UPDATE, component.ON_LAYOUT_CHANGE }) +local Scroll = component.create("scroll", { component.ON_INPUT, component.ON_UPDATE, component.ON_LAYOUT_CHANGE }) local function inverse_lerp(min, max, current) @@ -102,6 +103,8 @@ end -- @tfield[opt=0.2] number ANIM_SPEED Scroll gui.animation speed for scroll_to function -- @tfield[opt=0] number EXTRA_STRETCH_SIZE extra size in pixels outside of scroll (stretch effect) -- @tfield[opt=false] bool SMALL_CONTENT_SCROLL If true, content node with size less than view node size can be scrolled +-- @tfield[opt=25] bool WHEEL_SCROLL_SPEED The scroll speed via mouse wheel scroll or touchpad. Set to 0 to disable wheel scrolling +-- @tfield[opt=false] bool SMALL_CONTENT_SCROLL If true, invert direction for touchpad and mouse wheel scroll function Scroll.on_style_change(self, style) self.style = {} self.style.EXTRA_STRETCH_SIZE = style.EXTRA_STRETCH_SIZE or 0 @@ -115,6 +118,8 @@ function Scroll.on_style_change(self, style) self.style.INERT_SPEED = style.INERT_SPEED or 30 self.style.POINTS_DEADZONE = style.POINTS_DEADZONE or 20 self.style.SMALL_CONTENT_SCROLL = style.SMALL_CONTENT_SCROLL or false + self.style.WHEEL_SCROLL_SPEED = style.WHEEL_SCROLL_SPEED or 25 + self.style.WHEEL_SCROLL_INVERTED = style.WHEEL_SCROLL_INVERTED or false self._is_inert = not (self.style.FRICT == 0 or self.style.FRICT_HOLD == 0 or @@ -142,6 +147,10 @@ function Scroll.init(self, view_node, content_node) self.drag.on_touch_start:subscribe(self._on_touch_start) self.drag.on_touch_end:subscribe(self._on_touch_end) + self.hover = self.druid:new_hover(view_node) + self.hover.on_mouse_hover:subscribe(self._on_mouse_hover) + self._is_mouse_hover = false + self.on_scroll = Event() self.on_scroll_to = Event() self.on_point_scroll = Event() @@ -175,6 +184,11 @@ function Scroll.update(self, dt) end +function Scroll.on_input(self, action_id, action) + return self:_process_scroll_wheel(action_id, action) +end + + function Scroll.on_remove(self) self:bind_grid(nil) end @@ -703,4 +717,33 @@ function Scroll._update_params(self, dt) end +function Scroll._process_scroll_wheel(self, action_id, action) + if not self._is_mouse_hover or self.style.WHEEL_SCROLL_SPEED == 0 then + return false + end + + if action_id ~= const.ACTION_SCROLL_UP and action_id ~= const.ACTION_SCROLL_DOWN then + return false + end + + local koef = (action_id == const.ACTION_SCROLL_UP) and 1 or -1 + if self.style.WHEEL_SCROLL_INVERTED then + koef = -koef + end + + if self.drag.can_y then + self.inertion.y = (self.inertion.y + self.style.WHEEL_SCROLL_SPEED * koef) * self.style.FRICT_HOLD + else + self.inertion.x = (self.inertion.x + self.style.WHEEL_SCROLL_SPEED * koef) * self.style.FRICT_HOLD + end + + return true +end + + +function Scroll._on_mouse_hover(self, state) + self._is_mouse_hover = state +end + + return Scroll From 227d809c83ec73ac86320891940d3c8275a3f052 Mon Sep 17 00:00:00 2001 From: Insality Date: Thu, 1 Apr 2021 22:52:03 +0300 Subject: [PATCH 28/30] Update default page, update changelogs --- docs_md/changelog.md | 1 + example/gui/main/main.gui | 4 ++-- example/gui/main/main.gui_script | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs_md/changelog.md b/docs_md/changelog.md index f4f2d46..6dc7fde 100644 --- a/docs_md/changelog.md +++ b/docs_md/changelog.md @@ -157,3 +157,4 @@ local Drag = component.create("drag", { component.ON_INPUT }, const.PRIORITY_INP -- Mouse scroll working when cursor is hover on scroll view node -- Vertical scroll have more priority than horizontal -- Fix: When Hover component node became disabled, reset hover state (throw on_hover and on_mouse_hover events) +-- This is basic implementation, it is work not perfect diff --git a/example/gui/main/main.gui b/example/gui/main/main.gui index 0ddc821..cc3a6f9 100644 --- a/example/gui/main/main.gui +++ b/example/gui/main/main.gui @@ -5984,7 +5984,7 @@ nodes { nodes { position { x: -300.0 - y: 0.0 + y: 150.0 z: 0.0 w: 1.0 } @@ -6018,7 +6018,7 @@ nodes { id: "grid_content" xanchor: XANCHOR_NONE yanchor: YANCHOR_NONE - pivot: PIVOT_W + pivot: PIVOT_NW adjust_mode: ADJUST_MODE_FIT parent: "scroll_with_grid_size" layer: "image" diff --git a/example/gui/main/main.gui_script b/example/gui/main/main.gui_script index 7a4d589..c5b1348 100644 --- a/example/gui/main/main.gui_script +++ b/example/gui/main/main.gui_script @@ -72,7 +72,7 @@ function init(self) init_swipe_control(self) - self.page = 8 + self.page = 1 main_page.setup_page(self) text_page.setup_page(self) button_page.setup_page(self) From 001fc163eaed89e0e692619bae20893afe846956 Mon Sep 17 00:00:00 2001 From: Insality Date: Thu, 1 Apr 2021 23:03:28 +0300 Subject: [PATCH 29/30] Update changelog --- docs_md/changelog.md | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/docs_md/changelog.md b/docs_md/changelog.md index 6dc7fde..eb8c5af 100644 --- a/docs_md/changelog.md +++ b/docs_md/changelog.md @@ -140,21 +140,26 @@ Desc - Add EmmyLua annotations. See how to use it FAQ - Lang text now can be initialized without default locale id - **#116** You can pass Text component in Input component instead of text node -- **#124** Add _set_click_zone_ functon to Scroll component (just link to Drag:set_click_zone inside scroll component) -- **#102** __[BREAKING]__ Removed _increase_input_priority_ component function. Use _component:set_input_priority_ function instead. The bigger priority value processed first. The value 10 is default for Druid components, the 100 value is maximum priority for acquire input in _drag_ and _input_ components --- Add constants for priorities: _const.PRIORITY_INPUT_, _const.PRIORITY_INPUT_HIGH_, _const.PRIORITY_INPUT_MAX_. --- __[BREAKING]__ If you use in you custom components interest: __component.ON_INPUT_HIGH__ you should replace it with __const.PRIORITY_INPUT_HIGH__ as third param, and place it with usual __component.ON_INPUT__. For example -_before:_ -```lua -local Drag = component.create("drag", { component.ON_INPUT_HIGH }) -``` -_after:_ -```lua -local Drag = component.create("drag", { component.ON_INPUT }, const.PRIORITY_INPUT_HIGH) -``` +- **#124** Add `Scroll:set_click_zone` function. This is just link to `Drag:set_click_zone` function inside scroll component. +- **#102** __[BREAKING]__ Removed `component:increase_input_priority` component function. Use `component:set_input_priority` function instead. The bigger priority value processed first. The value 10 is default for Druid components, the 100 value is maximum priority for acquire input in _drag_ and _input_ components + -- Add constants for priorities: _const.PRIORITY_INPUT_, _const.PRIORITY_INPUT_HIGH_, _const.PRIORITY_INPUT_MAX_. + -- __[BREAKING]__ If you use in you custom components interest: `component.ON_INPUT_HIGH` you should replace it with `const.PRIORITY_INPUT_HIGH` as third param, and place it with usual `component.ON_INPUT`. For example: + _before:_ + ```lua + local Drag = component.create("drag", { component.ON_INPUT_HIGH }) + ``` + _after:_ + ```lua + local Drag = component.create("drag", { component.ON_INPUT }, const.PRIORITY_INPUT_HIGH) + ``` - **#123** Add scroll for Scroll component via mouse wheel or touchpad: --- Added Scroll style params: WHEEL_SCROLL_SPEED, WHEEL_SCROLL_INVERTED --- Mouse scroll working when cursor is hover on scroll view node --- Vertical scroll have more priority than horizontal --- Fix: When Hover component node became disabled, reset hover state (throw on_hover and on_mouse_hover events) --- This is basic implementation, it is work not perfect + -- Added Scroll style params: `WHEEL_SCROLL_SPEED`, `WHEEL_SCROLL_INVERTED` + -- Mouse scroll working when cursor is hover on scroll view node + -- Vertical scroll have more priority than horizontal + -- Fix: When Hover component node became disabled, reset hover state (throw on_hover and on_mouse_hover events) + -- This is basic implementation, it is work not perfect +- **#43** Add Data List Druid extended component. Component used to manage huge amount of data to make stuff like "infinity" scroll. +- Add context argument to Druid Event. You can pass this argument to forward it first in your callbacks (for example - object context) +- __[BREAKING]__ Add _SHIFT_POLICY_ for _Static_ and _Dynamic_ Grids. It mean how nodes will be shifted if you append data between nodes. There are `const.SHIFT.RIGHT`, `const.SHIFT.LEFT` and `const.SHIFT.NO_SHIFT`. + -- Please check your `StaticGrid:remove` and `DynamicGrid:remove` functions + From 295aca26a3c24b13e16cf95750e2f48df010eea9 Mon Sep 17 00:00:00 2001 From: Insality Date: Thu, 1 Apr 2021 23:16:16 +0300 Subject: [PATCH 30/30] Update docs --- docs_md/changelog.md | 1 + druid/base/scroll.lua | 6 +-- druid/extended/data_list.lua | 91 +++++++++++++++++++++++++++++++++--- 3 files changed, 89 insertions(+), 9 deletions(-) diff --git a/docs_md/changelog.md b/docs_md/changelog.md index eb8c5af..2d7de3a 100644 --- a/docs_md/changelog.md +++ b/docs_md/changelog.md @@ -157,6 +157,7 @@ Desc -- Mouse scroll working when cursor is hover on scroll view node -- Vertical scroll have more priority than horizontal -- Fix: When Hover component node became disabled, reset hover state (throw on_hover and on_mouse_hover events) + -- By default mouse scroll is disabled -- This is basic implementation, it is work not perfect - **#43** Add Data List Druid extended component. Component used to manage huge amount of data to make stuff like "infinity" scroll. - Add context argument to Druid Event. You can pass this argument to forward it first in your callbacks (for example - object context) diff --git a/druid/base/scroll.lua b/druid/base/scroll.lua index 5dcf2c8..ee548c7 100644 --- a/druid/base/scroll.lua +++ b/druid/base/scroll.lua @@ -103,8 +103,8 @@ end -- @tfield[opt=0.2] number ANIM_SPEED Scroll gui.animation speed for scroll_to function -- @tfield[opt=0] number EXTRA_STRETCH_SIZE extra size in pixels outside of scroll (stretch effect) -- @tfield[opt=false] bool SMALL_CONTENT_SCROLL If true, content node with size less than view node size can be scrolled --- @tfield[opt=25] bool WHEEL_SCROLL_SPEED The scroll speed via mouse wheel scroll or touchpad. Set to 0 to disable wheel scrolling --- @tfield[opt=false] bool SMALL_CONTENT_SCROLL If true, invert direction for touchpad and mouse wheel scroll +-- @tfield[opt=0] bool WHEEL_SCROLL_SPEED The scroll speed via mouse wheel scroll or touchpad. Set to 0 to disable wheel scrolling +-- @tfield[opt=false] bool WHEEL_SCROLL_INVERTED If true, invert direction for touchpad and mouse wheel scroll function Scroll.on_style_change(self, style) self.style = {} self.style.EXTRA_STRETCH_SIZE = style.EXTRA_STRETCH_SIZE or 0 @@ -118,7 +118,7 @@ function Scroll.on_style_change(self, style) self.style.INERT_SPEED = style.INERT_SPEED or 30 self.style.POINTS_DEADZONE = style.POINTS_DEADZONE or 20 self.style.SMALL_CONTENT_SCROLL = style.SMALL_CONTENT_SCROLL or false - self.style.WHEEL_SCROLL_SPEED = style.WHEEL_SCROLL_SPEED or 25 + self.style.WHEEL_SCROLL_SPEED = style.WHEEL_SCROLL_SPEED or 0 self.style.WHEEL_SCROLL_INVERTED = style.WHEEL_SCROLL_INVERTED or false self._is_inert = not (self.style.FRICT == 0 or diff --git a/druid/extended/data_list.lua b/druid/extended/data_list.lua index 7b51566..f9a22ae 100644 --- a/druid/extended/data_list.lua +++ b/druid/extended/data_list.lua @@ -1,5 +1,23 @@ ---- Manage data for huge dataset in scroll ---- It requires basic druid scroll and druid grid components +--- Component to manage data for huge dataset in scroll. +-- It requires Druid Scroll and Druid Grid (Static or Dynamic) components +-- @module DataList +-- @within BaseComponent +-- @alias druid.data_list + + +--- The Druid scroll component +-- @tfield Scroll scroll + +--- The Druid Grid component +-- @tfield StaticGrid grid + +--- The current visual top data index +-- @tfield number top_index + +--- The current visual last data index +-- @tfield number last_index + + local const = require("druid.const") local helper = require("druid.helper") local component = require("druid.component") @@ -7,9 +25,11 @@ local component = require("druid.component") local DataList = component.create("data_list") +--- Data list constructor +-- @tparam Scroll self +-- @tparam node view_node GUI view scroll node +-- @tparam node content_node GUI content scroll node function DataList.init(self, data, scroll, grid, create_function) - self.view_size = gui.get_size(scroll.view_node) - self.prefab_size = grid.node_size self.druid = self:get_druid() self.scroll = scroll self.grid = grid @@ -18,8 +38,8 @@ function DataList.init(self, data, scroll, grid, create_function) --- Current visual elements indexes self.top_index = 1 self.last_index = 1 - self.create_function = create_function + self._create_function = create_function self._data = {} self._data_first_index = false self._data_last_index = false @@ -32,11 +52,16 @@ function DataList.init(self, data, scroll, grid, create_function) end +--- Druid System on_remove function +-- @tparam DataList self function DataList.on_remove(self) self.scroll.on_scroll:unsubscribe(self._check_elements, self) end +--- Set new data set for DataList component +-- @tparam DataList self +-- @tparam table data The new data array function DataList.set_data(self, data) self._data = data self:_update_data_info() @@ -44,6 +69,12 @@ function DataList.set_data(self, data) end +--- Add element to DataList. Currenly untested +-- @tparam DataList self +-- @tparam table data +-- @tparam number index +-- @tparam number shift_policy The constant from const.SHIFT.* +-- @local function DataList.add(self, data, index, shift_policy) index = index or self._data_last_index + 1 shift_policy = shift_policy or const.SHIFT.RIGHT @@ -66,12 +97,22 @@ function DataList.add(self, data, index, shift_policy) end +--- Remove element from DataList. Currenly untested +-- @tparam DataList self +-- @tparam number index +-- @tparam number shift_policy The constant from const.SHIFT.* +-- @local function DataList.remove(self, index, shift_policy) table.remove(self._data, index) self:_refresh() end +--- Remove element from DataList by data value. Currenly untested +-- @tparam DataList self +-- @tparam tabe data +-- @tparam number shift_policy The constant from const.SHIFT.* +-- @local function DataList.remove_by_data(self, data, shift_policy) local index = helper.contains(self._data, data) if index then @@ -81,27 +122,38 @@ function DataList.remove_by_data(self, data, shift_policy) end +--- Clear the DataList and refresh visuals +-- @tparam DataList self function DataList.clear(self) self._data = {} self:_refresh() end +--- Return first index from data. It not always equals to 1 +-- @tparam DataList self function DataList.get_first_index(self) return self._data_first_index end +--- Return last index from data +-- @tparam DataList self function DataList.get_last_index(self) return self._data_last_index end +--- Return amount of data +-- @tparam DataList self function DataList.get_length(self) return self._data_length end +--- Return index for data value +-- @tparam DataList self +-- @tparam table data function DataList.get_index(self, data) for index, value in pairs(self._data) do if value == data then @@ -113,6 +165,9 @@ function DataList.get_index(self, data) end +--- Instant scroll to element with passed index +-- @tparam DataList self +-- @tparam number index function DataList.scroll_to_index(self, index) self.top_index = helper.clamp(index, 1, #self._data) self:_refresh() @@ -120,12 +175,16 @@ function DataList.scroll_to_index(self, index) end +--- Add element at passed index +-- @tparam DataList self +-- @tparam number index +-- @local function DataList._add_at(self, index) if self._data_visual[index] then self:_remove_at(index) end - local node, instance = self.create_function(self._data[index], index) + local node, instance = self._create_function(self._data[index], index) self.grid:add(node, index, const.SHIFT.NO_SHIFT) self._data_visual[index] = { node = node, @@ -134,6 +193,10 @@ function DataList._add_at(self, index) end +--- Remove element from passed index +-- @tparam DataList self +-- @tparam number index +-- @local function DataList._remove_at(self, index) self.grid:remove(index, const.SHIFT.NO_SHIFT) @@ -147,6 +210,9 @@ function DataList._remove_at(self, index) end +--- Fully refresh all DataList elements +-- @tparam DataList self +-- @local function DataList._refresh(self) for index, _ in pairs(self._data_visual) do self:_remove_at(index) @@ -155,6 +221,9 @@ function DataList._refresh(self) end +--- Check elements which should be created +-- @tparam DataList self +-- @local function DataList._check_elements(self) for index, data in pairs(self._data_visual) do if self.scroll:is_node_in_view(data.node) then @@ -173,6 +242,12 @@ function DataList._check_elements(self) end +--- Check elements which should be created. +-- Start from index with step until element is outside of scroll view +-- @tparam DataList self +-- @tparam number index +-- @tparam number step +-- @local function DataList._check_elements_from(self, index, step) local is_outside = false while not is_outside do @@ -202,6 +277,10 @@ function DataList._check_elements_from(self, index, step) end + +--- Update actual data params +-- @tparam DataList self +-- @local function DataList._update_data_info(self) self._data_first_index = false self._data_last_index = false