diff --git a/druid/base/scroll.lua b/druid/base/scroll.lua index 81d5e46..3850e12 100755 --- a/druid/base/scroll.lua +++ b/druid/base/scroll.lua @@ -143,7 +143,7 @@ function Scroll.init(self, view_node, content_node) self.view_border = helper.get_border(self.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.view_size = helper.get_scaled_size(self.view_node) self.position = gui.get_position(self.content_node) self.target_position = vmath.vector3(self.position) @@ -693,7 +693,7 @@ end 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)) + local content_size = helper.get_scaled_size(self.content_node) self.available_pos = get_border_vector(self.view_border - content_border, self._offset) self.available_size = get_size_vector(self.available_pos) diff --git a/druid/extended/dynamic_grid.lua b/druid/extended/dynamic_grid.lua index 0593715..5e8dbc6 100644 --- a/druid/extended/dynamic_grid.lua +++ b/druid/extended/dynamic_grid.lua @@ -118,7 +118,7 @@ function DynamicGrid.get_pos(self, index, node, origin_index) assert(not self.first_index, "Dynamic Grid can't have gaps between nodes. Error on grid:add") -- If not origin node, so it should be first element in the grid - local size = self:_get_node_size(node) + local size = helper.get_scaled_size(node) local pivot = const.PIVOTS[gui.get_pivot(node)] return vmath.vector3( size.x * pivot.x - size.x * self.pivot.x, @@ -308,7 +308,7 @@ function DynamicGrid._add_node(self, node, index, origin_index) self.nodes[index] = { node = node, pos = self:get_pos(index, node, origin_index), - size = self:_get_node_size(node), + size = helper.get_scaled_size(node), pivot = const.PIVOTS[gui.get_pivot(node)] } @@ -394,7 +394,7 @@ end function DynamicGrid._get_next_node_pos(self, origin_node_index, new_node, place_side) local node = self.nodes[origin_node_index] - local new_node_size = self:_get_node_size(new_node) + local new_node_size = helper.get_scaled_size(new_node) local new_pivot = const.PIVOTS[gui.get_pivot(new_node)] local dist_x = (node.size.x/2 + new_node_size.x/2) * place_side.x @@ -410,11 +410,6 @@ 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 - - --- 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 9f2c621..2b3ceb4 100644 --- a/druid/helper.lua +++ b/druid/helper.lua @@ -1,6 +1,6 @@ -- Copyright (c) 2021 Maksim Tuprikov . This code is licensed under MIT license ---- Druid helper module for gui layouts +--- Helper module with various usefull GUI functions. -- @module Helper -- @alias druid.helper @@ -9,7 +9,6 @@ local const = require("druid.const") local M = {} ---- Text node or icon node can be nil local function get_text_width(text_node) if text_node then local text_metrics = M.get_text_metrics_from_node(text_node) @@ -31,6 +30,7 @@ local function get_icon_width(icon_node) end +--- Text node or icon node can be nil local function get_width(node) return gui.get_text(node) and get_text_width(node) or get_icon_width(node) end @@ -43,6 +43,7 @@ end -- @tparam[opt] text text_node Gui text node -- @tparam[opt] box icon_node Gui box node -- @tparam number margin Offset between nodes +-- @local function M.centrate_text_with_icon(text_node, icon_node, margin) M.centrate_nodes(margin, text_node, icon_node) end @@ -55,6 +56,7 @@ end -- @tparam[opt] box icon_node Gui box node -- @tparam[opt] text text_node Gui text node -- @tparam[opt=0] number margin Offset between nodes +-- @local function M.centrate_icon_with_text(icon_node, text_node, margin) M.centrate_nodes(margin, icon_node, text_node) end @@ -98,6 +100,10 @@ function M.centrate_nodes(margin, ...) end +--- Get current screen stretch multiplier for each side +-- @function helper.get_screen_aspect_koef +-- @treturn number stretch_x +-- @treturn number stretch_y function M.get_screen_aspect_koef() local window_x, window_y = window.get_size() local stretch_x = window_x / gui.get_width() @@ -107,6 +113,10 @@ function M.get_screen_aspect_koef() end +--- Get current GUI scale for each side +-- @function helper.get_gui_scale +-- @treturn number scale_x +-- @treturn number scale_y function M.get_gui_scale() local window_x, window_y = window.get_size() return math.min(window_x / gui.get_width(), @@ -114,6 +124,12 @@ function M.get_gui_scale() end +--- Move value from current to target value with step amount +-- @function helper.step +-- @tparam number current Current value +-- @tparam number target Target value +-- @tparam number step Step amount +-- @treturn number New value function M.step(current, target, step) if current < target then return math.min(current + step, target) @@ -123,6 +139,12 @@ function M.step(current, target, step) end +--- Clamp value between min and max +-- @function helper.clamp +-- @tparam number a Value +-- @tparam number min Min value +-- @tparam number max Max value +-- @treturn number Clamped value function M.clamp(a, min, max) if min > max then min, max = max, min @@ -138,11 +160,22 @@ function M.clamp(a, min, max) end +--- Calculate distance between two points +-- @function helper.distance +-- @tparam number x1 First point x +-- @tparam number y1 First point y +-- @tparam number x2 Second point x +-- @tparam number y2 Second point y +-- @treturn number Distance function M.distance(x1, y1, x2, y2) return math.sqrt((x2 - x1) ^ 2 + (y2 - y1) ^ 2) end +--- Return sign of value (-1, 0, 1) +-- @function helper.sign +-- @tparam number val Value +-- @treturn number Sign function M.sign(val) if val == 0 then return 0 @@ -152,27 +185,47 @@ function M.sign(val) end -function M.round(num, numDecimalPlaces) - local mult = 10^(numDecimalPlaces or 0) +--- Round number to specified decimal places +-- @function helper.round +-- @tparam number num Number +-- @tparam[opt=0] number num_decimal_places Decimal places +-- @treturn number Rounded number +function M.round(num, num_decimal_places) + local mult = 10^(num_decimal_places or 0) return math.floor(num * mult + 0.5) / mult end +--- Lerp between two values +-- @function helper.lerp +-- @tparam number a First value +-- @tparam number b Second value +-- @tparam number t Lerp amount +-- @treturn number Lerped value function M.lerp(a, b, t) return a + (b - a) * t end +--- Check if value is in array and return index of it +-- @function helper.contains +-- @tparam table t Array +-- @param value Value +-- @treturn number|nil Index of value or nil function M.contains(t, value) for i = 1, #t do if t[i] == value then return i end end - return false + return nil end +--- Make a copy table with all nested tables +-- @function helper.deepcopy +-- @tparam table orig_table Original table +-- @treturn table Copy of original table function M.deepcopy(orig_table) local orig_type = type(orig_table) local copy @@ -188,35 +241,23 @@ function M.deepcopy(orig_table) end ---- Get text metric from gui node. Replacement of previous gui.get_text_metrics_from_node function --- @tparam Node text_node --- @treturn table {width, height, max_ascent, max_descent} -function M.get_text_metrics_from_node(text_node) - local font_name = gui.get_font(text_node) - local font = gui.get_font_resource(font_name) - return resource.get_text_metrics(font, gui.get_text(text_node), { - width = gui.get_size(text_node).x, - leading = gui.get_leading(text_node), - tracking = gui.get_tracking(text_node), - line_break = gui.get_line_break(text_node), - }) +--- Get node size adjusted by scale +-- @function helper.get_scaled_size +-- @tparam node node GUI node +-- @treturn vector3 Scaled size +function M.get_scaled_size(node) + return vmath.mul_per_elem(gui.get_size(node), gui.get_scale(node)) end ---- Check if node is enabled in gui hierarchy. +--- Check if node is enabled in GUI hierarchy. +-- -- Return false, if node or any his parent is disabled -- @function helper.is_enabled --- @tparam node node Gui node +-- @tparam node node GUI node -- @treturn bool Is enabled in hierarchy function M.is_enabled(node) - local is_enabled = gui.is_enabled(node) - local parent = gui.get_parent(node) - while parent and is_enabled do - is_enabled = is_enabled and gui.is_enabled(parent) - parent = gui.get_parent(parent) - end - - return is_enabled + return gui.is_enabled(node, true) end @@ -237,10 +278,10 @@ function M.get_scene_scale(node, include_passed_node_scale) end ---- Return closest non inverted clipping parent node for node +--- Return closest non inverted clipping parent node for given node -- @function helper.get_closest_stencil_node --- @tparam node node Gui node --- @treturn node|nil The clipping node +-- @tparam node node GUI node +-- @treturn node|nil The closest stencil node or nil function M.get_closest_stencil_node(node) if not node then return nil @@ -262,17 +303,20 @@ function M.get_closest_stencil_node(node) end ---- Get node offset for given gui pivot +--- Get node offset for given GUI pivot. +-- +-- Offset shown in [-0.5 .. 0.5] range, where -0.5 is left or bottom, 0.5 is right or top. -- @function helper.get_pivot_offset -- @tparam gui.pivot pivot The node pivot --- @treturn vector3 Vector offset with [-1..1] values +-- @treturn vector3 Vector offset with [-0.5..0.5] values function M.get_pivot_offset(pivot) return const.PIVOTS[pivot] end ---- Check if device is mobile (Android or iOS) +--- Check if device is native mobile (Android or iOS) -- @function helper.is_mobile +-- @treturn bool Is mobile function M.is_mobile() return const.CURRENT_SYSTEM_NAME == const.OS.IOS or const.CURRENT_SYSTEM_NAME == const.OS.ANDROID @@ -281,12 +325,14 @@ end --- Check if device is HTML5 -- @function helper.is_web +-- @treturn bool Is web function M.is_web() return const.CURRENT_SYSTEM_NAME == const.OS.BROWSER end ---- Transform table to oneline string +--- Simple table to one-line string converter +-- @function helper.table_to_string -- @tparam table t -- @treturn string function M.table_to_string(t) @@ -309,13 +355,13 @@ end --- Distance from node position to his borders -- @function helper.get_border --- @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) +-- @tparam node node GUI node +-- @tparam[opt] vector3 offset Offset from node position. Pass current node position to get non relative border values +-- @treturn vector4 Vector4 with border values (left, top, right, down) 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)) + local size = M.get_scaled_size(node) local border = vmath.vector4( -size.x*(0.5 + pivot_offset.x), size.y*(0.5 - pivot_offset.y), @@ -334,9 +380,10 @@ function M.get_border(node, offset) end ---- Get text metric from gui node. Replacement of previous gui.get_text_metrics_from_node function +--- Get text metric from GUI node. +-- @function helper.get_text_metrics_from_node -- @tparam Node text_node --- @treturn table {width, height, max_ascent, max_descent} +-- @treturn GUITextMetrics Fields: width, height, max_ascent, max_descent function M.get_text_metrics_from_node(text_node) local font_resource = gui.get_font_resource(gui.get_font(text_node)) local options = { @@ -354,6 +401,15 @@ function M.get_text_metrics_from_node(text_node) end +--- Add value to array with shift policy +-- +-- Shift policy can be: left, right, no_shift +-- @function helper.insert_with_shift +-- @tparam table array Array +-- @param item Item to insert +-- @tparam[opt] number index Index to insert. If nil, item will be inserted at the end of array +-- @tparam[opt] const.SHIFT shift_policy Shift policy +-- @treturn item Inserted item function M.insert_with_shift(array, item, index, shift_policy) shift_policy = shift_policy or const.SHIFT.RIGHT @@ -377,6 +433,14 @@ function M.insert_with_shift(array, item, index, shift_policy) end +--- Remove value from array with shift policy +-- +-- Shift policy can be: left, right, no_shift +-- @function helper.remove_with_shift +-- @tparam table array Array +-- @tparam[opt] number index Index to remove. If nil, item will be removed from the end of array +-- @tparam[opt] const.SHIFT shift_policy Shift policy +-- @treturn item Removed item function M.remove_with_shift(array, index, shift_policy) shift_policy = shift_policy or const.SHIFT.RIGHT @@ -404,6 +468,7 @@ end --- Show deprecated message. Once time per message -- @function helper.deprecated -- @tparam string message The deprecated message +-- @local local _deprecated_messages = {} function M.deprecated(message) if _deprecated_messages[message] then @@ -415,7 +480,8 @@ function M.deprecated(message) end --- Show message to require extended component +--- Show message to require extended component +-- @local function M.extended_component(component_name) print(string.format("[Druid]: The component %s is extended component. You have to register it via druid.register to use it", component_name)) print("[Druid]: Use next code:") diff --git a/druid/system/druid_instance.lua b/druid/system/druid_instance.lua index 9942f2f..ece0568 100755 --- a/druid/system/druid_instance.lua +++ b/druid/system/druid_instance.lua @@ -16,7 +16,7 @@ -- -- @{BaseComponent} - The parent class of all Druid components. You can find all default component methods there. -- --- Other important information: +-- # Tech Info # -- -- • To use Druid, you need to create a Druid instance first. This instance is used to spawn components. -- @@ -253,7 +253,7 @@ function DruidInstance.initialize(self, context, style) end ---- Create new component +--- Create new component. -- @tparam DruidInstance self -- @tparam Component component Component module -- @tparam args ... Other component params to pass it to component:init function @@ -271,8 +271,7 @@ function DruidInstance.new(self, component, ...) end ---- Call on final function on gui_script. It will call on_remove --- on all druid components +--- Call this in gui_script final function. -- @tparam DruidInstance self function DruidInstance.final(self) local components = self.components_all @@ -289,7 +288,8 @@ function DruidInstance.final(self) end ---- Remove component from druid instance. +--- Remove component from Druid instance. +-- -- Component `on_remove` function will be invoked, if exist. -- @tparam DruidInstance self -- @tparam Component component Component instance @@ -333,7 +333,7 @@ function DruidInstance.remove(self, component) end ---- Druid late update function called after initialization and before the regular update step +--- Druid late update function called after initialization and before the regular update step -- This function is used to check the GUI state and perform actions after all components and nodes have been created. -- An example use case is performing an auto stencil check in the GUI hierarchy for input components. -- @tparam DruidInstance self @@ -353,6 +353,7 @@ end --- Call this in gui_script update function. +-- -- Used for: scroll, progress, timer components -- @tparam DruidInstance self -- @tparam number dt Delta time @@ -370,6 +371,7 @@ end --- Call this in gui_script on_input function. +-- -- Used for almost all components -- @tparam DruidInstance self -- @tparam hash action_id Action_id from on_input @@ -390,6 +392,7 @@ end --- Call this in gui_script on_message function. +-- -- Used for special actions. See SPECIFIC_UI_MESSAGES table -- @tparam DruidInstance self -- @tparam hash message_id Message_id from on_message diff --git a/update_docs.sh b/update_docs.sh index 34ca68d..e469afb 100755 --- a/update_docs.sh +++ b/update_docs.sh @@ -13,4 +13,5 @@ echo "Update EmmyLua annotations" original_path=$(pwd) bash $emmylua_generator_path/export.sh $original_path mv $emmylua_generator_path/annotations.lua $original_path/druid/annotations.lua +cat ./utils/annotations_manual.lua >> $original_path/druid/annotations.lua cp $original_path/utils/ldoc_fixed.css $original_path/docs/ldoc_fixed.css diff --git a/utils/annotations_manual.lua b/utils/annotations_manual.lua index 8259dd5..70b269e 100644 --- a/utils/annotations_manual.lua +++ b/utils/annotations_manual.lua @@ -1,3 +1,5 @@ +-- Manual Annotations -- + ---@class druid.rich_text.metrics ---@field width number ---@field height number @@ -49,3 +51,9 @@ ---@field default_animation string ---@field node_prefab Node ---@field text_prefab Node + +---@class GUITextMetrics +---@field width number +---@field height number +---@field max_ascent number +---@field max_descent number