mirror of
https://github.com/Insality/druid.git
synced 2025-09-27 18:12:19 +02:00
Add class component and druid_instance
This commit is contained in:
@@ -2,52 +2,12 @@
|
||||
--@class component
|
||||
|
||||
local const = require("druid.const")
|
||||
local class = require("druid.system.middleclass")
|
||||
|
||||
local M = {}
|
||||
local instance = {}
|
||||
local Component = class("druid.component")
|
||||
|
||||
|
||||
function instance.get_style(self)
|
||||
if not self._meta.style then
|
||||
return const.EMPTY_TABLE
|
||||
end
|
||||
|
||||
return self._meta.style[self._component.name] or const.EMPTY_TABLE
|
||||
end
|
||||
|
||||
|
||||
function instance.set_style(self, druid_style)
|
||||
self._meta.style = druid_style
|
||||
end
|
||||
|
||||
|
||||
function instance.set_template(self, template)
|
||||
self._meta.template = template
|
||||
end
|
||||
|
||||
|
||||
function instance.set_nodes(self, nodes)
|
||||
self._meta.nodes = nodes
|
||||
end
|
||||
|
||||
|
||||
function instance.get_context(self, context)
|
||||
return self._meta.context
|
||||
end
|
||||
|
||||
|
||||
function instance.set_context(self, context)
|
||||
self._meta.context = context
|
||||
end
|
||||
|
||||
|
||||
function instance.get_druid(self)
|
||||
local context = { _context = self }
|
||||
return setmetatable(context, { __index = self:get_context().druid })
|
||||
end
|
||||
|
||||
|
||||
function instance.setup_component(self, context, style)
|
||||
function Component.setup_component(self, context, style)
|
||||
self._meta = {
|
||||
template = nil,
|
||||
context = nil,
|
||||
@@ -62,17 +22,99 @@ function instance.setup_component(self, context, style)
|
||||
end
|
||||
|
||||
|
||||
function M.new(name, interest)
|
||||
local mt = {
|
||||
_component = {
|
||||
name = name,
|
||||
interest = interest
|
||||
}
|
||||
}
|
||||
local component = setmetatable(mt, { __index = instance })
|
||||
function Component.get_style(self)
|
||||
if not self._meta.style then
|
||||
return const.EMPTY_TABLE
|
||||
end
|
||||
|
||||
return component
|
||||
return self._meta.style[self._component.name] or const.EMPTY_TABLE
|
||||
end
|
||||
|
||||
|
||||
return M
|
||||
function Component.set_style(self, druid_style)
|
||||
self._meta.style = druid_style
|
||||
end
|
||||
|
||||
|
||||
function Component.get_template(self)
|
||||
return self._meta.template
|
||||
end
|
||||
|
||||
|
||||
function Component.set_template(self, template)
|
||||
self._meta.template = template
|
||||
end
|
||||
|
||||
|
||||
function Component.get_nodes(self)
|
||||
return self._meta.nodes
|
||||
end
|
||||
|
||||
|
||||
function Component.set_nodes(self, nodes)
|
||||
self._meta.nodes = nodes
|
||||
end
|
||||
|
||||
|
||||
function Component.get_context(self, context)
|
||||
return self._meta.context
|
||||
end
|
||||
|
||||
|
||||
function Component.set_context(self, context)
|
||||
self._meta.context = context
|
||||
end
|
||||
|
||||
|
||||
function Component.get_interests(self)
|
||||
return self._component.interest
|
||||
end
|
||||
|
||||
|
||||
-- TODO: Определиться с get_node и node
|
||||
-- get_node - берет ноду по ноде или строке
|
||||
-- node - может брать ноду у компонента по схеме (если есть
|
||||
-- template или таблица нод после gui.clone_tree)
|
||||
function Component.get_node(self, node_or_name)
|
||||
local template_name = self:get_template() or const.EMPTY_STRING
|
||||
local nodes = self:get_nodes()
|
||||
|
||||
if nodes then
|
||||
return nodes[template_name .. node_or_name]
|
||||
else
|
||||
if type(node_or_name) == const.STRING then
|
||||
return gui.get_node(template_name .. node_or_name)
|
||||
else
|
||||
return node_or_name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Component.get_druid(self)
|
||||
local context = { _context = self }
|
||||
return setmetatable(context, { __index = self:get_context().druid })
|
||||
end
|
||||
|
||||
|
||||
function Component.initialize(self, name, interest)
|
||||
self._component = {
|
||||
name = name,
|
||||
interest = interest
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
function Component.static.create(name, interest)
|
||||
-- Yea, inheritance here
|
||||
local new_class = class(name, Component)
|
||||
|
||||
new_class.initialize = function(self)
|
||||
Component.initialize(self, name, interest)
|
||||
end
|
||||
|
||||
return new_class
|
||||
end
|
||||
|
||||
|
||||
return Component
|
||||
|
@@ -1,8 +1,9 @@
|
||||
local const = require("druid.const")
|
||||
local druid_input = require("druid.helper.druid_input")
|
||||
local settings = require("druid.system.settings")
|
||||
local class = require("druid.system.middleclass")
|
||||
|
||||
local M = {}
|
||||
local Druid = class("druid.druid_instance")
|
||||
|
||||
|
||||
local function input_init(self)
|
||||
@@ -19,16 +20,15 @@ end
|
||||
|
||||
|
||||
-- Create the component
|
||||
local function create(self, module)
|
||||
local function create(self, instance_class)
|
||||
---@class component
|
||||
local instance = setmetatable({}, { __index = module })
|
||||
local instance = instance_class()
|
||||
|
||||
-- Component context, self from component creation
|
||||
instance:setup_component(self._context, self._style)
|
||||
|
||||
table.insert(self.all_components, instance)
|
||||
|
||||
local register_to = module._component.interest
|
||||
local register_to = instance:get_interests()
|
||||
if register_to then
|
||||
for i = 1, #register_to do
|
||||
local interest = register_to[i]
|
||||
@@ -47,51 +47,6 @@ local function create(self, module)
|
||||
end
|
||||
|
||||
|
||||
function M.new(self, module, ...)
|
||||
local instance = create(self, module)
|
||||
|
||||
if instance.init then
|
||||
instance:init(...)
|
||||
end
|
||||
|
||||
return instance
|
||||
end
|
||||
|
||||
|
||||
function M.remove(self, instance)
|
||||
for i = #self.all_components, 1, -1 do
|
||||
if self.all_components[i] == instance then
|
||||
table.remove(self, i)
|
||||
end
|
||||
end
|
||||
|
||||
local interests = instance._component.interest
|
||||
if interests then
|
||||
for i = 1, #interests do
|
||||
local interest = interests[i]
|
||||
local array = self.components[interest]
|
||||
for j = #array, 1, -1 do
|
||||
if array[j] == instance then
|
||||
table.remove(array, j)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Druid instance update function
|
||||
-- @function druid:update(dt)
|
||||
function M.update(self, dt)
|
||||
local array = self.components[const.ON_UPDATE]
|
||||
if array then
|
||||
for i = 1, #array do
|
||||
array[i]:update(dt)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function notify_input_on_swipe(self)
|
||||
if self.components[const.ON_INPUT] then
|
||||
local len = #self.components[const.ON_INPUT]
|
||||
@@ -118,17 +73,76 @@ local function match_event(action_id, events)
|
||||
end
|
||||
|
||||
|
||||
--- Druid constructor
|
||||
function Druid.initialize(self, context, style)
|
||||
self._context = context
|
||||
self._style = style or settings.default_style
|
||||
self.all_components = {}
|
||||
self.components = {}
|
||||
end
|
||||
|
||||
|
||||
--- Create new component inside druid instance
|
||||
function Druid.create(self, instance_class, ...)
|
||||
local instance = create(self, instance_class)
|
||||
|
||||
if instance.init then
|
||||
instance:init(...)
|
||||
end
|
||||
|
||||
return instance
|
||||
end
|
||||
|
||||
|
||||
--- Remove component from druid instance
|
||||
-- It will call on_remove on component, if exist
|
||||
function Druid.remove(self, component)
|
||||
for i = #self.all_components, 1, -1 do
|
||||
if self.all_components[i] == component then
|
||||
if component.on_remove then
|
||||
component:on_remove()
|
||||
end
|
||||
table.remove(self, i)
|
||||
end
|
||||
end
|
||||
|
||||
local interests = component:get_interests()
|
||||
if interests then
|
||||
for i = 1, #interests do
|
||||
local interest = interests[i]
|
||||
local array = self.components[interest]
|
||||
for j = #array, 1, -1 do
|
||||
if array[j] == component then
|
||||
table.remove(array, j)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Druid instance update function
|
||||
-- @function druid:update(dt)
|
||||
function Druid.update(self, dt)
|
||||
local array = self.components[const.ON_UPDATE]
|
||||
if array then
|
||||
for i = 1, #array do
|
||||
array[i]:update(dt)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Druid instance on_input function
|
||||
-- @function druid:on_input(action_id, action)
|
||||
function M.on_input(self, action_id, action)
|
||||
function Druid.on_input(self, action_id, action)
|
||||
-- TODO: расписать отличия ON_SWIPE и ON_INPUT
|
||||
-- Почему-то некоторые используют ON_SWIPE, а логичнее ON_INPUT? (blocker, slider)
|
||||
local array = self.components[const.ON_SWIPE]
|
||||
if array then
|
||||
local v, result
|
||||
local len = #array
|
||||
for i = len, 1, -1 do
|
||||
v = array[i]
|
||||
local result
|
||||
for i = #array, 1, -1 do
|
||||
local v = array[i]
|
||||
result = result or v:on_input(action_id, action)
|
||||
end
|
||||
if result then
|
||||
@@ -139,42 +153,38 @@ function M.on_input(self, action_id, action)
|
||||
|
||||
array = self.components[const.ON_INPUT]
|
||||
if array then
|
||||
local v
|
||||
local len = #array
|
||||
for i = len, 1, -1 do
|
||||
v = array[i]
|
||||
for i = #array, 1, -1 do
|
||||
local v = array[i]
|
||||
if match_event(action_id, v.event) and v:on_input(action_id, action) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--- Druid instance on_message function
|
||||
-- @function druid:on_message(message_id, message, sender)
|
||||
function M.on_message(self, message_id, message, sender)
|
||||
function Druid.on_message(self, message_id, message, sender)
|
||||
local specific_ui_message = const.SPECIFIC_UI_MESSAGES[message_id]
|
||||
if specific_ui_message then
|
||||
local array = self.components[message_id]
|
||||
if array then
|
||||
local item
|
||||
for i = 1, #array do
|
||||
item = array[i]
|
||||
local item = array[i]
|
||||
item[specific_ui_message](item, message, sender)
|
||||
end
|
||||
end
|
||||
else
|
||||
local array = self.components[const.ON_MESSAGE]
|
||||
if array then
|
||||
for i = 1, #array do
|
||||
array[i]:on_message(message_id, message, sender)
|
||||
end
|
||||
local array = self.components[const.ON_MESSAGE] or const.EMPTY_TABLE
|
||||
for i = 1, #array do
|
||||
array[i]:on_message(message_id, message, sender)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return M
|
||||
return Druid
|
||||
|
183
druid/system/middleclass.lua
Normal file
183
druid/system/middleclass.lua
Normal file
@@ -0,0 +1,183 @@
|
||||
local middleclass = {
|
||||
_VERSION = 'middleclass v4.1.1',
|
||||
_DESCRIPTION = 'Object Orientation for Lua',
|
||||
_URL = 'https://github.com/kikito/middleclass',
|
||||
_LICENSE = [[
|
||||
MIT LICENSE
|
||||
|
||||
Copyright (c) 2011 Enrique García Cota
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
}
|
||||
|
||||
local function _createIndexWrapper(aClass, f)
|
||||
if f == nil then
|
||||
return aClass.__instanceDict
|
||||
else
|
||||
return function(self, name)
|
||||
local value = aClass.__instanceDict[name]
|
||||
|
||||
if value ~= nil then
|
||||
return value
|
||||
elseif type(f) == "function" then
|
||||
return (f(self, name))
|
||||
else
|
||||
return f[name]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function _propagateInstanceMethod(aClass, name, f)
|
||||
f = name == "__index" and _createIndexWrapper(aClass, f) or f
|
||||
aClass.__instanceDict[name] = f
|
||||
|
||||
for subclass in pairs(aClass.subclasses) do
|
||||
if rawget(subclass.__declaredMethods, name) == nil then
|
||||
_propagateInstanceMethod(subclass, name, f)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function _declareInstanceMethod(aClass, name, f)
|
||||
aClass.__declaredMethods[name] = f
|
||||
|
||||
if f == nil and aClass.super then
|
||||
f = aClass.super.__instanceDict[name]
|
||||
end
|
||||
|
||||
_propagateInstanceMethod(aClass, name, f)
|
||||
end
|
||||
|
||||
local function _tostring(self) return "class " .. self.name end
|
||||
local function _call(self, ...) return self:new(...) end
|
||||
|
||||
local function _createClass(name, super)
|
||||
local dict = {}
|
||||
dict.__index = dict
|
||||
|
||||
local aClass = { name = name, super = super, static = {},
|
||||
__instanceDict = dict, __declaredMethods = {},
|
||||
subclasses = setmetatable({}, {__mode='k'}) }
|
||||
|
||||
if super then
|
||||
setmetatable(aClass.static, {
|
||||
__index = function(_,k)
|
||||
local result = rawget(dict,k)
|
||||
if result == nil then
|
||||
return super.static[k]
|
||||
end
|
||||
return result
|
||||
end
|
||||
})
|
||||
else
|
||||
setmetatable(aClass.static, { __index = function(_,k) return rawget(dict,k) end })
|
||||
end
|
||||
|
||||
setmetatable(aClass, { __index = aClass.static, __tostring = _tostring,
|
||||
__call = _call, __newindex = _declareInstanceMethod })
|
||||
|
||||
return aClass
|
||||
end
|
||||
|
||||
local function _includeMixin(aClass, mixin)
|
||||
assert(type(mixin) == 'table', "mixin must be a table")
|
||||
|
||||
for name,method in pairs(mixin) do
|
||||
if name ~= "included" and name ~= "static" then aClass[name] = method end
|
||||
end
|
||||
|
||||
for name,method in pairs(mixin.static or {}) do
|
||||
aClass.static[name] = method
|
||||
end
|
||||
|
||||
if type(mixin.included)=="function" then mixin:included(aClass) end
|
||||
return aClass
|
||||
end
|
||||
|
||||
local DefaultMixin = {
|
||||
__tostring = function(self) return "instance of " .. tostring(self.class) end,
|
||||
|
||||
initialize = function(self, ...) end,
|
||||
|
||||
isInstanceOf = function(self, aClass)
|
||||
return type(aClass) == 'table'
|
||||
and type(self) == 'table'
|
||||
and (self.class == aClass
|
||||
or type(self.class) == 'table'
|
||||
and type(self.class.isSubclassOf) == 'function'
|
||||
and self.class:isSubclassOf(aClass))
|
||||
end,
|
||||
|
||||
static = {
|
||||
allocate = function(self)
|
||||
assert(type(self) == 'table', "Make sure that you are using 'Class:allocate' instead of 'Class.allocate'")
|
||||
return setmetatable({ class = self }, self.__instanceDict)
|
||||
end,
|
||||
|
||||
new = function(self, ...)
|
||||
assert(type(self) == 'table', "Make sure that you are using 'Class:new' instead of 'Class.new'")
|
||||
local instance = self:allocate()
|
||||
instance:initialize(...)
|
||||
return instance
|
||||
end,
|
||||
|
||||
subclass = function(self, name)
|
||||
assert(type(self) == 'table', "Make sure that you are using 'Class:subclass' instead of 'Class.subclass'")
|
||||
assert(type(name) == "string", "You must provide a name(string) for your class")
|
||||
|
||||
local subclass = _createClass(name, self)
|
||||
|
||||
for methodName, f in pairs(self.__instanceDict) do
|
||||
_propagateInstanceMethod(subclass, methodName, f)
|
||||
end
|
||||
subclass.initialize = function(instance, ...) return self.initialize(instance, ...) end
|
||||
|
||||
self.subclasses[subclass] = true
|
||||
self:subclassed(subclass)
|
||||
|
||||
return subclass
|
||||
end,
|
||||
|
||||
subclassed = function(self, other) end,
|
||||
|
||||
isSubclassOf = function(self, other)
|
||||
return type(other) == 'table' and
|
||||
type(self.super) == 'table' and
|
||||
( self.super == other or self.super:isSubclassOf(other) )
|
||||
end,
|
||||
|
||||
include = function(self, ...)
|
||||
assert(type(self) == 'table', "Make sure you that you are using 'Class:include' instead of 'Class.include'")
|
||||
for _,mixin in ipairs({...}) do _includeMixin(self, mixin) end
|
||||
return self
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
function middleclass.class(name, super)
|
||||
assert(type(name) == 'string', "A name (string) is needed for the new class")
|
||||
return super and super:subclass(name) or _includeMixin(_createClass(name), DefaultMixin)
|
||||
end
|
||||
|
||||
setmetatable(middleclass, { __call = function(_, ...) return middleclass.class(...) end })
|
||||
|
||||
return middleclass
|
@@ -9,13 +9,11 @@ M.auto_focus_gain = true
|
||||
|
||||
|
||||
function M.get_text(name)
|
||||
-- override to get text for localized text
|
||||
return "[Druid]: locales not inited"
|
||||
end
|
||||
|
||||
|
||||
function M.play_sound(name)
|
||||
-- override to play sound with name
|
||||
end
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user