luajitos

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

dom.lua (3871B)


      1 -- DOM (Document Object Model)
      2 -- Handles nested elements with positioning, dimensions, and click handling
      3 
      4 local DOM = {}
      5 DOM.__index = DOM
      6 
      7 -- Element class
      8 local Element = {}
      9 Element.__index = Element
     10 
     11 function Element:new(tag, attrs)
     12     attrs = attrs or {}
     13 
     14     return setmetatable({
     15         tag = tag or "div",
     16         id = attrs.id,
     17         classes = attrs.class and self:parse_classes(attrs.class) or {},
     18         attributes = attrs,
     19         children = {},
     20         parent = nil,
     21         content = "",
     22 
     23         -- Layout properties (computed during layout)
     24         x = 0,
     25         y = 0,
     26         width = 0,
     27         height = 0,
     28 
     29         -- Event handlers
     30         handlers = {}
     31     }, self)
     32 end
     33 
     34 function Element:parse_classes(class_string)
     35     local classes = {}
     36     for class in class_string:gmatch("%S+") do
     37         table.insert(classes, class)
     38     end
     39     return classes
     40 end
     41 
     42 function Element:has_class(class_name)
     43     for _, cls in ipairs(self.classes) do
     44         if cls == class_name then
     45             return true
     46         end
     47     end
     48     return false
     49 end
     50 
     51 function Element:add_child(child)
     52     table.insert(self.children, child)
     53     child.parent = self
     54     return child
     55 end
     56 
     57 function Element:set_content(content)
     58     self.content = content
     59 end
     60 
     61 function Element:set_layout(x, y, width, height)
     62     self.x = x
     63     self.y = y
     64     self.width = width
     65     self.height = height
     66 end
     67 
     68 function Element:contains_point(px, py)
     69     return px >= self.x and px < self.x + self.width and
     70            py >= self.y and py < self.y + self.height
     71 end
     72 
     73 function Element:on(event, handler)
     74     self.handlers[event] = handler
     75 end
     76 
     77 function Element:trigger(event, ...)
     78     local handler = self.handlers[event]
     79     if handler then
     80         return handler(self, ...)
     81     end
     82     return false
     83 end
     84 
     85 -- Walk the tree in paint order (depth-first, reverse order so last child is on top)
     86 function Element:find_element_at(px, py)
     87     -- Check children in reverse order (last child painted on top)
     88     for i = #self.children, 1, -1 do
     89         local child = self.children[i]
     90         local found = child:find_element_at(px, py)
     91         if found then
     92             return found
     93         end
     94     end
     95 
     96     -- Check if this element contains the point
     97     if self:contains_point(px, py) then
     98         return self
     99     end
    100 
    101     return nil
    102 end
    103 
    104 function Element:debug_print(indent)
    105     indent = indent or 0
    106     local spaces = string.rep("  ", indent)
    107     local class_str = #self.classes > 0 and (" ." .. table.concat(self.classes, ".")) or ""
    108     local id_str = self.id and ("#" .. self.id) or ""
    109     print(string.format("%s<%s%s%s> [%d,%d %dx%d]",
    110         spaces, self.tag, id_str, class_str, self.x, self.y, self.width, self.height))
    111 
    112     for _, child in ipairs(self.children) do
    113         child:debug_print(indent + 1)
    114     end
    115 end
    116 
    117 -- DOM tree manager
    118 function DOM:new()
    119     return setmetatable({
    120         root = nil
    121     }, self)
    122 end
    123 
    124 function DOM:create_element(tag, attrs)
    125     return Element:new(tag, attrs)
    126 end
    127 
    128 function DOM:set_root(element)
    129     self.root = element
    130 end
    131 
    132 function DOM:click(x, y)
    133     if not self.root then
    134         return false
    135     end
    136 
    137     -- Find the topmost element at this position
    138     local element = self.root:find_element_at(x, y)
    139 
    140     if not element then
    141         return false
    142     end
    143 
    144     -- Bubble up from the clicked element to root
    145     local current = element
    146     while current do
    147         -- Trigger click event
    148         local blocked = current:trigger("click", x, y)
    149         if blocked then
    150             -- Event was handled and blocked from propagating
    151             return true
    152         end
    153 
    154         current = current.parent
    155     end
    156 
    157     return false
    158 end
    159 
    160 function DOM:debug_print()
    161     if self.root then
    162         print("=== DOM Tree ===")
    163         self.root:debug_print()
    164     else
    165         print("=== DOM Tree (empty) ===")
    166     end
    167 end
    168 
    169 return {
    170     DOM = DOM,
    171     Element = Element
    172 }