diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 76ac7f9..6764eb4 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -37,6 +37,7 @@ jobs:
- name: setup common
run: |
+ luarocks install lpath
luarocks install oocairo
luarocks --server=https://luarocks.org/dev install lpugl
luarocks --server=https://luarocks.org/dev install lpugl_cairo
@@ -49,7 +50,9 @@ jobs:
run: |
set -e
lua -v
+ lua src/alltests.lua src/tests
cd src
- lua alltests.lua
- lua doctest.lua ../doc/*md
+ export LUA_PATH='./?.lua;./?/init.lua;'$LUA_PATH
+ echo "LUA_PATH=$LUA_PATH"
+ lua doctest.lua
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 20ca93b..f293826 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
/.files
+*.trace
+
diff --git a/README.md b/README.md
index bc8add9..afe95d9 100644
--- a/README.md
+++ b/README.md
@@ -32,11 +32,11 @@ of standard widgets. So far only very simple standard widgets are provided, e.g.
## First Example
-* The first example demonstrates a simple dialog using the `lwtk.TextInput` widget.
+* The first example demonstrates a simple "Hello World" dialog.
The appearance of the widgets is configured in [lwtk.DefaultStyle](src/lwtk/DefaultStyle.lua).
The key bindings are configured in [lwtk.DefaultKeyBinding](src/lwtk/DefaultKeyBinding.lua).
- ![Screenshot example01](./example/screenshot01.png)
+ ![Screenshot example01](./example/screenshot00.png)
```lua
local lwtk = require("lwtk")
@@ -45,67 +45,27 @@ of standard widgets. So far only very simple standard widgets are provided, e.g.
local Column = lwtk.Column
local Row = lwtk.Row
local PushButton = lwtk.PushButton
- local TextInput = lwtk.TextInput
local TitleText = lwtk.TitleText
local Space = lwtk.Space
- local app = Application("example01.lua")
+ local app = Application("example")
local function quit()
app:close()
end
local win = app:newWindow {
- title = "example01",
+ title = "example",
Column {
- id = "c1",
- TitleText { text = "What's your name?" },
- TextInput { id = "i1", focus = true, style = { Columns = 40 } },
+ TitleText { text = "Hello World!", style = { textSize = 35 } },
Row {
Space {},
- PushButton { id = "b1", text = "&OK", disabled = true,
- default = true },
-
- PushButton { id = "b2", text = "&Quit", onClicked = quit },
+ PushButton { text = "&OK", onClicked = quit },
Space {}
}
},
- Column {
- id = "c2",
- visible = false,
- Space {},
- TitleText { id = "t2", style = { TextAlign = "center" } },
- Space {},
- Row {
- Space {},
- PushButton { id = "b3", text = "&Again" },
-
- PushButton { id = "b4", text = "&Quit", default = true,
- onClicked = quit },
- Space {}
- }
- }
}
-
- win:childById("c1"):setOnInputChanged(function(widget, input)
- widget:childById("b1"):setDisabled(input.text == "")
- end)
-
- win:childById("b1"):setOnClicked(function(widget)
- win:childById("t2"):setText("Hello "..win:childById("i1").text.."!")
- win:childById("c1"):setVisible(false)
- win:childById("c2"):setVisible(true)
- end)
-
- win:childById("b3"):setOnClicked(function(widget)
- win:childById("i1"):setText("")
- win:childById("i1"):setFocus()
- win:childById("c1"):setVisible(true)
- win:childById("c2"):setVisible(false)
- end)
-
win:show()
-
app:runEventLoop()
```
diff --git a/doc/Application.md b/doc/Application.md
index 81648d4..b52ff32 100644
--- a/doc/Application.md
+++ b/doc/Application.md
@@ -1,53 +1,16 @@
-# lwtk.Application
+# lwtk.Application Usage
-TODO
-
-
-## Contents
-
-
- * [Application Methods](#application-methods)
- * [Application:runEventLoop()](#Application_runEventLoop)
- * [Application:update()](#Application_update)
-
-
+[lwtk.Application] objects are needed to control lwtk event processing and window
+management for desktop applications.
+[lwtk.Application]: ./gen/lwtk/Application.md
-## Application Methods
-
-
-* **`Application:runEventLoop(timeout)
- `**
-
- Update by processing events from the window system.
-
- * *timeout* - optional float, timeout in seconds
-
- If *timeout* is given, this function will process events from the window system until
- the time period in seconds has elapsed or until all window objects have been closed.
-
- If *timeout* is `nil` or not given, this function will process events from the window system
- until all window objects have been closed.
-
+## Contents
-* **`Application:update(timeout)
- `**
+TODO
- Update by processing events from the window system.
-
- * *timeout* - optional float, timeout in seconds
-
- If *timeout* is given, this function will wait for *timeout* seconds until
- events from the window system become available. If *timeout* is `nil` or not
- given, this function will block indefinitely until an event occurs.
-
- As soon as events are available, all events in the queue are processed and this function
- returns `true`.
-
- If *timeout* is given and there are no events available after *timeout*
- seconds, this function will return `false`.
diff --git a/doc/Class.md b/doc/Class.md
index d4f0b3e..3f2caea 100644
--- a/doc/Class.md
+++ b/doc/Class.md
@@ -1,53 +1,79 @@
-# lwtk.Class
+# lwtk.Class Usage
-Each lwtk *object* is actually a Lua table that has a lwtk *class* as metatable. The class
-is also called the object's *type*.
+Like [lwtk.Meta] objects, [lwtk.Class] objects are factories for creating derived objects
+of a certain type. The thereby derived objects are belonging to a type hierarchy of super
+classes providing inheritance mechanismen.
+
+
+[lwtk.Meta]: ./gen/lwtk/Meta.md
+[lwtk.Class]: ./gen/lwtk/Class.md
+[lwtk.Mixin]: ./gen/lwtk/Mixin.md
## Contents
- * [Declaring Classes](#declaring-classes)
- * [Instantiating Objects](#instantiating-objects)
- * [Declaring Methods](#declaring-methods)
- * [Declaring Derived Classes](#declaring-derived-classes)
+ * [Creating Classes](#creating-classes)
* [Instantiating Derived Objects](#instantiating-derived-objects)
- * [Overriding Methods](#overriding-methods)
+ * [Declaring Members](#declaring-members)
+ * [Defining Methods](#defining-methods)
+ * [Creating Subclasses](#creating-subclasses)
+ * [Instantiating Derived Objects from Subclass](#instantiating-derived-objects-from-subclass)
+ * [Overriding Members](#overriding-members)
+ * [Implementing Members](#implementing-members)
+ * [Constructors](#constructors)
+ * [Static Members](#static-members)
+ * [Extra Members](#extra-members)
-## Declaring Classes
+## Creating Classes
-Let's start by declaring a new class named `"Foo"`:
+Let's start by creating a new class object named `"Foo"`:
```lua
local lwtk = require("lwtk")
local Foo = lwtk.newClass("Foo")
```
-The new class `Foo` is actually a Lua table that has `lwtk.Class` as metatable.
-Its `tostring` value is the class name `"Foo"` and `lwtk.type()` evaluates to `"lwtk.Class"`:
+The new class object `Foo` is actually a Lua table that has [lwtk.Class] as metatable.
+Its `tostring` value is `"lwtk.Class"` and *[lwtk.type()]* evaluates to `"lwtk.Class"`:
+
+[lwtk.type()]: ./gen/lwtk/type.md
```lua
assert(type(Foo) == "table")
assert(getmetatable(Foo) == lwtk.Class)
-assert(tostring(Foo) == "Foo")
+assert(tostring(Foo) == "lwtk.Class")
assert(lwtk.type(Foo) == "lwtk.Class")
```
+
+
+The class name is only used for debugging purposes: it is allowed to create two
+different class objects with the same name (although this is not recommended):
+
+```lua
+local Foo2 = lwtk.newClass("Foo")
+assert(Foo ~= Foo2)
+assert(tostring(Foo) == tostring(Foo2))
+```
-## Instantiating Objects
+## Instantiating Derived Objects
-Objects of a certain class are instantiated by calling the class:
+The class object is used to instantiate new derived objects by simply calling the class object:
```lua
local o1 = Foo()
local o2 = Foo()
```
-The created objects are Lua tables that have `Foo` as metatable. Their `tostring` value
-contains the class name `"Foo"` and `lwtk.type()` evaluates to `"Foo"`:
+The instantiated objects are Lua tables that have the class object `Foo` as metatable.
+Their `tostring` value contains the class name `"Foo"` and *[lwtk.type()]* evaluates
+to `"Foo"`:
```lua
for _, o in ipairs{o1, o2} do
@@ -58,116 +84,223 @@ for _, o in ipairs{o1, o2} do
end
assert(o1 ~= o2)
```
+
+
+**Technical details**
+
+Strictly speaking, the class object is not the real metatable, but gives access to
+the underlying metatable, which can be observed by using the Lua `debug` package:
+```lua
+local realMt = debug.getmetatable(o1)
+assert(realMt == debug.getmetatable(o2))
+assert(realMt ~= Foo)
+assert(realMt.__index == Foo.__index)
+assert(realMt.__newindex == Foo.__newindex)
+```
-## Declaring Methods
+## Declaring Members
-Methods declared for the class table become methods for all objects of this type:
+Members of the class object become available in the derived objects:
```lua
-function Foo:setX(x)
- self._x = x
-end
-function Foo:getX(x)
- return self._x
-end
-o1:setX(100)
-o2:setX(200)
-assert(o1:getX() == 100)
-assert(o2:getX() == 200)
-assert(o1._x == 100)
-assert(o2._x == 200)
+Foo.x = 100
+Foo.y = false
+assert(o1.x == 100)
+assert(o2.x == 100)
+assert(o1.y == false)
+assert(o2.y == false)
+```
+
+Members of the class object may not be changed once they are declared:
+
+```lua
+local ok, err = pcall(function() Foo.x = 999 end)
+assert(not ok and err:match('member "x" already defined in class'))
+```
+
+Througout lwtk, a _defined_ member denotes a member having a value that is
+not `false` and not `nil`. A _declared_ member denotes a member having a value
+that is not `nil`. So in our example, member `x` is defined (and declared) and
+member `y` is only declared:
+
+```lua
+local ok, err = pcall(function() Foo.y = 999 end)
+assert(not ok and err:match('member "y" already declared in class'))
+```
+
+If a member value is changed in a derived object, it does not effect the
+member value in the underlying class object or in other derived objects:
+
+```lua
+o1.x = 200
+assert(o1.x == 200)
+assert(o2.x == 100)
+assert(Foo.x == 100)
+```
+
+It is not allowed to get or set members in a derived object that
+are not declared in the underlying class object:
+
+```lua
+local ok, err = pcall(function() o1.z = 300 end)
+assert(not ok and err:match('member "z" not declared in class'))
+
+local ok, err = pcall(function() print(o1.z) end)
+assert(not ok and err:match('member "z" not declared in class'))
+```
+
+Therefore all object members have to be declared in the underlying
+class object. This can be done by setting them to an initial value (as
+seen above) or by using the `declare` helper method, that sets the
+given members to the value `false`:
+
+```lua
+Foo:declare("a", "b")
+o1.a = "A1"
+o2.a = "A2"
+assert(Foo.a == false)
+assert(Foo.b == false)
+assert(o1.a == "A1")
+assert(o2.a == "A2")
+```
+
+**Technical details**
+
+Members are retrieved by metatable lookup from the underlying class object until
+they are overwritten in the derived object:
+
+```lua
+Foo.bar = {}
+assert(o1.bar == Foo.bar)
+assert(rawget(o1, "bar") == nil)
+assert(rawget(Foo, "bar") == nil)
+assert(rawget(Foo.__index, "bar") == Foo.bar)
+o1.bar = {}
+assert(o1.bar ~= Foo.bar)
+assert(rawget(o1, "bar") == o1.bar)
```
-The methods are retrieved by metatable lookup from the class:
+
+## Defining Methods
+
+
+Methods are members of type `function` which are defined using Lua's member syntax:
```lua
-assert(rawget(o1, "setX") == nil)
-assert(rawget(Foo, "setX") == Foo.setX)
-assert(o1.setX == Foo.setX)
+function Foo:setX(x)
+ self.x = x
+end
+function Foo:getX()
+ return self.x
+end
+o1:setX(10)
+o2:setX(20)
+assert(o1:getX() == 10)
+assert(o2:getX() == 20)
+assert(o1.x == 10)
+assert(o2.x == 20)
```
-## Declaring Derived Classes
+## Creating Subclasses
-Let's create a derived class named `"Bar"` that has `Foo` as super class:
+Let's create a new subclass named `"Bar"` that has `Foo` as superclass:
```lua
local Bar = lwtk.newClass("Bar", Foo)
```
-Every class declared by `lwtk.newClass()` has a super class,
-default super class is `lwtk.Object`:
+Every class declared by `lwtk.newClass()` has a superclass,
+default superclass is `lwtk.Object`:
```lua
-assert(lwtk.getSuperClass(Bar) == Foo)
-assert(lwtk.getSuperClass(Foo) == lwtk.Object)
-assert(lwtk.getSuperClass(lwtk.Object) == nil)
+assert(Bar:getSuperClass() == Foo)
+assert(Foo:getSuperClass() == lwtk.Object)
+assert(lwtk.Object:getSuperClass() == nil)
+assert(Bar:getClassPath() == "Bar(Foo(lwtk.Object))")
+assert(Bar:getReverseClassPath() == "/lwtk.Object/Foo/Bar")
```
-The new class object is a Lua table, its `tostring` value is the class name `"Bar"`,
-`lwtk.type()` evaluates to `"lwtk.Class"` and has `lwtk.Class` as metatable:
+The new class `Bar` is a Lua table that has `lwtk.Class` as metatable.
+Its `tostring` value is `"lwtk.Class"` and `lwtk.type()` evaluates to `"lwtk.Class"`:
```lua
assert(type(Bar) == "table")
-assert(tostring(Bar) == "Bar")
-assert(lwtk.type(Bar) == "lwtk.Class")
assert(getmetatable(Bar) == lwtk.Class)
+assert(tostring(Bar) == "lwtk.Class")
+assert(lwtk.type(Bar) == "lwtk.Class")
```
-The derived class contains the methods of the super class at the time when `newClass`
+The subclass contains the members of the superclass at the time when `newClass`
was invoked:
```lua
-assert(rawget(Bar, "setX") == Foo.setX)
-assert(rawget(Bar, "getX") == Foo.getX)
+assert(rawget(Bar.__index, "setX") == Foo.setX)
+assert(rawget(Bar.__index, "getX") == Foo.getX)
```
-Methods declared for `Foo` after the creation of class `Bar` have no effect:
+Members declared for `Foo` after the creation of class `Bar` have no effect:
```lua
-function Foo:setY(y)
- self._y = y
-end
-assert(Bar.setY == nil)
-assert(Foo.setY ~= nil)
+Foo.newMember = {}
+local ok, err = pcall(function() print(Bar.newMember) end)
+assert(not ok and err:match('no member "newMember" in class'))
+assert(rawget(Foo.__index, "newMember") == Foo.newMember)
+assert(rawget(Bar.__index, "newMember") == nil)
+assert(rawget(Bar, "newMember") == nil)
```
-## Instantiating Derived Objects
+## Instantiating Derived Objects from Subclass
-Let's instantiate an object of the derived class `Bar`:
+Let's instantiate a derived object of the subclass `Bar`:
```lua
local o3 = Bar()
```
-The created object is a Lua table, the `tostring` value contains the class name `"Bar"`,
-`lwtk.type()` evaluates to `"Bar"` and its metatable is `Bar`:
-
+The instantiated object is a Lua table, it has the class object `Bar` as metatable.
+The `tostring` value contains the class name `"Bar"` and *[lwtk.type()]* evaluates
+to `"Bar"`:
```lua
assert(type(o3) == "table")
+assert(getmetatable(o3) == Bar)
assert(tostring(o3):match("^Bar: [xa-fA-F0-9]+$")) -- e.g. "Bar: 0x55d1e35c2430"
assert(lwtk.type(o3) == "Bar")
-assert(getmetatable(o3) == Bar)
```
-The created object has the methods of the super class:
+The instantiated object has the members of the superclass:
```lua
assert(o3.setX == Foo.setX)
-assert(o3.setY == nil)
o3:setX(300)
assert(o3:getX()== 300)
-assert(o3._x == 300)
+assert(o3.x == 300)
+```
+
+Members declared for the subclass are only available for subclass derived objects:
+
+
+```lua
+function Bar:addX(x)
+ return self.x + x
+end
+assert(o3:addX(2) == 302)
+
+local ok, err = pcall(function() print(o2.addX) end)
+assert(not ok and err:match('member "addX" not declared in class'))
```
+
Use `lwtk.isInstanceOf()` to check if an object is an instance of the specified class:
```lua
@@ -183,32 +316,254 @@ assert(lwtk.isInstanceOf(o2, lwtk.Object))
-## Overriding Methods
+## Overriding Members
-Methods declared for the derived class are only available for derived objects:
+For going on, let's instantiate new class objects:
+
+```lua
+local Foo = lwtk.newClass("Foo")
+do
+ Foo.x = 100
+ Foo.y = false
+ function Foo:getX()
+ return self.x
+ end
+end
+local Bar = lwtk.newClass("Bar", Foo)
+```
+It's not allowed to simply declare a member in a subclass that is alread declared in the
+superclass:
```lua
-function Bar:addX(x)
- return self._x + x
+
+local ok, err = pcall(function() Bar.x = false end)
+assert(not ok and err:match('member "x" .* is already defined in superclass'))
+
+local ok, err = pcall(function() Bar:declare("x") end)
+assert(not ok and err:match('member "x" .* is already defined in superclass'))
+
+local ok, err = pcall(function() Bar.y = 999 end)
+assert(not ok and err:match('member "y" .* is already declared in superclass'))
+
+local ok, err = pcall(function() Bar:declare("y") end)
+assert(not ok and err:match('member "y" .* is already declared in superclass'))
+```
+
+To indicate that overriding a member of the superclass is intentional,
+a special `override` syntax has to be used:
+
+```lua
+local t = {}
+
+Bar.override.x = false
+Bar.override.y = t
+
+local foo = Foo()
+local bar = Bar()
+
+assert(foo.x == 100 and foo.y == false)
+assert(bar.x == false and bar.y == t)
+
+assert(Foo.x == 100 and Foo.y == false)
+assert(Bar.x == false and Bar.y == t)
+```
+
+Each class object may have a special `override` table containing the
+overridden members:
+
+```lua
+for k, v in pairs(Bar.override) do
+ assert(k == "x" and v == false
+ or k == "y" and v == t)
end
-assert(o2.addX == nil)
-assert(o3.addX ~= nil)
-assert(o3:addX(2) == 302)
+```
+
+This example demonstrates an overriding method calling the
+overridden super method:
+
+```lua
+function Bar.override:getX()
+ return 2 * Foo.getX(self) -- double the value from the superclass
+end
+foo.x = 100
+bar.x = 200
+assert(foo:getX() == 100) -- invokes Foo.getX
+assert(bar:getX() == 400) -- invokes Bar.getX
+```
+
+
+## Implementing Members
+
+
+Use the `implement` table to ensure that the overridden member has no
+definition in the superclass. This is especially useful for methods where it
+could be crucial to know that a superclass implementation needs not to be
+considered:
+
+```lua
+local Foo = lwtk.newClass("Foo")
+do
+ function Foo:m1()
+ return 100
+ end
+
+ Foo.m2 = false
+
+ function Foo:m3()
+ return 300
+ end
+
+ Foo.m4 = false
+end
+
+local Bar = lwtk.newClass("Bar", Foo)
+function Bar.override:m1()
+ return 1000 + Foo.m1(self) -- consider superclass method
+end
+
+function Bar.implement:m2()
+ return 2000 -- no m2 in superclass
+end
```
-Let's override the method `getX` for the derived class:
+It is an error to implement members that are defined in the superclass:
```lua
-function Bar:getX()
- return 2 * Foo.getX(self) -- double the value from the super class
+local ok, err = pcall(function()
+ function Bar.implement:m3()
+ return 3000
+ end
+end)
+assert(not ok and err:match('member "m3" .* already defined in superclass'))
+```
+
+It is also an error to implement members that are not declared in the superclass:
+
+```lua
+local ok, err = pcall(function()
+ function Bar.implement:m3a()
+ return 3000
+ end
+end)
+assert(not ok and err:match('cannot implement member "m3a"'))
+```
+
+It is possible to override superclass members that are only declared and not
+defined in the superclass (this is especially useful for implementing objects of
+type [lwtk.Mixin]):
+
+```lua
+function Bar.override:m4()
+ if Foo.m4 then
+ return 4000 + Foo.m4(self) -- consider superclass method
+ end
end
-assert(o1:getX() == 100) -- invokes Foo.getX
-assert(o3:getX() == 600) -- invokes Bar.getX
+
```
+
+## Constructors
+
+
+The construction of new derived objects can be customized by implementing the method `new` in
+the class object. The `new` method receives the newly created derived object as `self` argument.
+
+```lua
+local Foo = lwtk.newClass("Foo")
+do
+ Foo.x = false
+ function Foo:new(x)
+ self.x = x
+ end
+end
+local o1 = Foo(100)
+local o2 = Foo(200)
+assert(o1.x == 100)
+assert(o2.x == 200)
+```
+
+The `new` method can also be overriden like normal methods:
+
+```lua
+local Bar = lwtk.newClass("Bar", Foo)
+do
+ function Bar.override:new(x)
+ Foo.new(self, 2 * x) -- call superclass new
+ end
+end
+local o3 = Bar(300)
+assert(o3.x == 600)
+```
+
+
+
+## Static Members
+
+
+A table named `static` can be used to declare members that are only available in the
+class object and not in the derived objects:
+
+```lua
+local Foo = lwtk.newClass("Foo")
+local t = {}
+Foo.static.T = t
+assert(Foo.T == t)
+
+local foo = Foo() -- derived object
+
+local ok, err = pcall(function() print(foo.T) end)
+assert(not ok and err:match('member "T" not declared'))
+
+local ok, err = pcall(function() print(foo.static.T) end)
+assert(not ok and err:match('member "static" not declared'))
+```
+
+Such static members are inherited by subclasses like normal members:
+```lua
+local Bar = lwtk.newClass("Bar", Foo)
+assert(Bar.T == t)
+assert(Bar.static.T == t)
+```
+
+
+## Extra Members
+
+
+The `extra` table can be used to declare members that are only available
+in the class object's extra table and are not inherited by subclasses
+and also not in derived objects:
+
+```lua
+local Foo = lwtk.newClass("Foo")
+local t = {}
+Foo.extra.T = t
+
+local ok, err = pcall(function() print(Foo.T) end)
+assert(not ok and err:match('no member "T" in class'))
+
+local Bar = lwtk.newClass("Bar", Foo)
+assert(Bar.extra.T == nil)
+assert(Foo.extra.T == t)
+
+local foo = Foo() -- derived object
+local bar = Bar() -- derived object
+
+local ok, err = pcall(function() print(foo.T) end)
+assert(not ok and err:match('member "T" not declared in class'))
+
+local ok, err = pcall(function() print(foo.extra.T) end)
+assert(not ok and err:match('member "extra" not declared in class'))
+
+local ok, err = pcall(function() print(bar.T) end)
+assert(not ok and err:match('member "T" not declared in class'))
+
+local ok, err = pcall(function() print(bar.extra.T) end)
+assert(not ok and err:match('member "extra" not declared in class'))
+
+```
+
diff --git a/doc/Meta.md b/doc/Meta.md
new file mode 100644
index 0000000..6062dd1
--- /dev/null
+++ b/doc/Meta.md
@@ -0,0 +1,131 @@
+# lwtk.Meta Usage
+
+[lwtk.Meta] objects are factories for creating derived objects that are sharing the
+same Lua metatable. They are lightweight wrappers around the standard Lua
+metatable mechanism.
+
+[lwtk.Meta]: ./gen/lwtk/Meta.md
+
+
+## Contents
+
+
+ * [Creating Meta Objects](#creating-meta-objects)
+ * [Instantiating Derived Objects](#instantiating-derived-objects)
+ * [Constructors](#constructors)
+ * [Example](#example)
+
+
+## Creating Meta Objects
+
+
+Let's start by creating a new meta object named `"Foo"`:
+
+```lua
+local lwtk = require("lwtk")
+local Foo = lwtk.newMeta("Foo")
+```
+
+The new meta object `Foo` is actually a Lua table that has [lwtk.Meta] as metatable.
+Its `tostring` value is `"lwtk.Meta"` and *[lwtk.type()]* evaluates to `"lwtk.Meta"`:
+
+[lwtk.type()]: ./gen/lwtk/type.md
+
+```lua
+assert(type(Foo) == "table")
+assert(getmetatable(Foo) == lwtk.Meta)
+assert(tostring(Foo) == "lwtk.Meta")
+assert(lwtk.type(Foo) == "lwtk.Meta")
+```
+
+
+
+The meta object's name is only used for debugging purposes: it is allowed to create two
+different meta objects with the same name (although this is not recommended):
+
+```lua
+local Foo2 = lwtk.newMeta("Foo")
+assert(Foo ~= Foo2)
+assert(tostring(Foo) == tostring(Foo2))
+```
+
+
+## Instantiating Derived Objects
+
+
+The meta object is used to create new derived objects by simply calling the meta object:
+
+```lua
+local o1 = Foo()
+local o2 = Foo()
+```
+
+The thereby created objects are Lua tables that have `Foo` as metatable. Their `tostring` value
+contains the meta name `"Foo"` and *[lwtk.type()]* evaluates to `"Foo"`:
+
+```lua
+for _, o in ipairs{o1, o2} do
+ assert(type(o) == "table")
+ assert(getmetatable(o) == Foo)
+ assert(tostring(o):match("^Foo: [xa-fA-F0-9]+$")) -- e.g. "Foo: 0x55d1e35c2430"
+ assert(lwtk.type(o) == "Foo")
+end
+assert(o1 ~= o2)
+```
+
+
+
+## Constructors
+
+
+The construction of new derived objects can be customized by implementing the method `new` in
+the meta object. The `new` method receives the newly created derived object as `self` argument.
+
+```lua
+function Foo:new(arg)
+ assert(getmetatable(self) == Foo)
+ self.arg = arg
+end
+local o3 = Foo(300)
+local o4 = Foo(400)
+assert(o3.arg == 300)
+assert(o4.arg == 400)
+```
+
+
+## Example
+
+
+Meta objects can be used to create enriched objects by implementing standard Lua metamethods:
+
+```lua
+local Vector = lwtk.newMeta("Vector")
+
+function Vector:new(x, y)
+ self.x = x
+ self.y = y
+end
+
+function Vector:__add(other)
+ return Vector(self.x + other.x,
+ self.y + other.y)
+end
+
+function Vector:__tostring()
+ return string.format("Vector(%d,%d)", self.x, self.y)
+end
+
+local v1 = Vector(100,10)
+local v2 = Vector(200,20)
+
+assert(tostring(v1 + v2) == "Vector(300,30)")
+```
+
+
+
diff --git a/doc/Mixin.md b/doc/Mixin.md
new file mode 100644
index 0000000..efca9c9
--- /dev/null
+++ b/doc/Mixin.md
@@ -0,0 +1,193 @@
+# lwtk.Mixin Usage
+
+[lwtk.Mixin] objects can be used to inject reusable functionality into new [lwtk.Class]
+objects.
+
+[lwtk.Meta]: ./gen/lwtk/Meta.md
+[lwtk.Class]: ./gen/lwtk/Class.md
+[lwtk.Mixin]: ./gen/lwtk/Mixin.md
+
+
+## Contents
+
+
+ * [Creating Mixins](#creating-mixins)
+ * [Deriving Classes from Mixins](#deriving-classes-from-mixins)
+ * [First Example](#first-example)
+ * [Referring to Superclasses](#referring.to-superclasses)
+
+
+## Creating Mixins
+
+
+Let's start by creating a new mixin object named `"Foo"`:
+
+```lua
+local lwtk = require("lwtk")
+local Foo = lwtk.newMixin("Foo")
+```
+
+The new mixin object `Foo` is actually a Lua table that has [lwtk.Mixin] as metatable.
+Its `tostring` value is `"lwtk.Mixin"` and *[lwtk.type()]* evaluates to `"lwtk.Mixin"`:
+
+[lwtk.type()]: ./gen/lwtk/type.md
+
+```lua
+assert(type(Foo) == "table")
+assert(getmetatable(Foo) == lwtk.Mixin)
+assert(tostring(Foo) == "lwtk.Mixin")
+assert(lwtk.type(Foo) == "lwtk.Mixin")
+```
+
+
+The mixin object's name is only used for debugging purposes: it is allowed to create two
+different mixin objects with the same name (although this is not recommended):
+
+```lua
+local Foo2 = lwtk.newMixin("Foo")
+assert(Foo ~= Foo2)
+assert(tostring(Foo) == tostring(Foo2))
+```
+
+
+## Deriving Classes from Mixins
+
+
+A new class is created by calling the mixin object:
+
+```lua
+local C1 = Foo()
+assert(lwtk.type(C1) == "lwtk.Class")
+assert(tostring(C1) == "lwtk.Class")
+```
+This call is cached, i.e. calling the mixin object again leads to the same class object:
+
+```lua
+assert(C1 == Foo())
+```
+
+Objects derived by calling the class have as type name the name `Foo()`:
+
+```lua
+local o1 = C1()
+assert(lwtk.type(o1) == "Foo()")
+assert(tostring(o1):match("^Foo%(%): [xa-fA-F0-9]+$")) -- e.g. "Foo(): 0x55d1e35c2430"
+assert(o1:getClass() == C1)
+assert(o1:getSuperClass() == lwtk.Object)
+```
+
+A base class may be given to the mixin object's call to derive a new class with the
+base class as superclass:
+
+```lua
+local Base = lwtk.newClass("Base")
+local C2 = Foo(Base)
+assert(lwtk.type(C2) == "lwtk.Class")
+assert(tostring(C2) == "lwtk.Class")
+assert(C2 == Foo(Base))
+assert(C2:getSuperClass() == Base)
+```
+
+```lua
+local o2 = C2()
+assert(lwtk.type(o2) == "Foo(Base)")
+assert(tostring(o2):match("^Foo%(Base%): [xa-fA-F0-9]+$")) -- e.g. "Foo(Base): 0x55d1e35c2430"
+assert(o2:getClass() == C2)
+assert(o2:getSuperClass() == Base)
+```
+
+The mixin class name is marked with a `#` character in the class path:
+
+```lua
+assert(C1:getClassPath() == "#Foo(lwtk.Object)")
+assert(C2:getClassPath() == "#Foo(Base(lwtk.Object))")
+```
+
+
+## First Example
+
+
+```lua
+local Base1 = lwtk.newClass("Base1")
+local Base2 = lwtk.newClass("Base2")
+
+local Colored = lwtk.newMixin("Colored")
+do
+ Colored.color = false
+ function Colored:setColor(c)
+ self.color = c
+ end
+ function Colored:getColor(c)
+ return self.color
+ end
+end
+
+local Sub1 = lwtk.newClass("Sub1", Colored(Base1))
+local Sub2 = lwtk.newClass("Sub2", Colored(Base2))
+
+assert(Sub1:getClassPath() == "Sub1(#Colored(Base1(lwtk.Object)))")
+assert(Sub2:getClassPath() == "Sub2(#Colored(Base2(lwtk.Object)))")
+
+local s1 = Sub1()
+local s2 = Sub2()
+
+s1:setColor("red")
+assert(s1:getColor() == "red")
+
+s2:setColor("blue")
+assert(s2:getColor() == "blue")
+
+assert(Sub1:getSuperClass() == Colored(Base1))
+
+```
+
+
+
+## Referring to Superclasses
+
+
+The effective superclass is unknown at the time when a new mixin object is created. A function
+may be given to `lwtk.newMixin()` that is called when the mixin object is called to create
+a new class object. This function is called with the concrete derived class object and
+the superclass:
+
+```lua
+local MyMixin = lwtk.newMixin("MyMixin",
+ function(MyMixin, Super)
+ assert(lwtk.type(MyMixin) == "lwtk.Class")
+ assert(tostring(MyMixin):match("^lwtk.Class$"))
+ function MyMixin.override:getX()
+ return 1000 + Super.getX(self)
+ end
+ end
+)
+function MyMixin:getY()
+ return 2000
+end
+function Base1:getX()
+ return 10
+end
+function Base2:getX()
+ return 20
+end
+local Class1 = lwtk.newClass("Class1", MyMixin(Base1))
+local Class2 = lwtk.newClass("Class2", MyMixin(Base2))
+local c1 = Class1()
+local c2 = Class2()
+assert(c1:getX() == 1010)
+assert(c2:getX() == 1020)
+assert(c1:getY() == 2000)
+assert(c2:getY() == 2000)
+```
+
+
+
+
+
diff --git a/doc/README.md b/doc/README.md
index f6b621a..5d3ad8f 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -1,7 +1,14 @@
# lwtk Documentation
-TODO
- * [lwtk.Class](Class.md) - declaring classes and instantiate objects
- * [lwtk.Application](Application.md) - handling windows and events
- * [lwtk.Style](Style.md) - configuring widget styles
+## Overview
+ * [Introduction](Introduction.md) - using lwtk modules. submodules and types
+ * [lwtk.Meta Usage](Meta.md) - simplified metatable handling
+ * [lwtk.Class Usage](Class.md) - declaring classes and instantiate objects
+ * [lwtk.Mixin Usage](Mixin.md) - resusable class extensions
+ * [lwtk.Application Usage](Application.md) - handling windows and events
+ * [lwtk.Style Usage](Style.md) - configuring widget styles
* TODO
+
+## Reference
+ * [Module Index](./gen/modules.md) - list of all lwtk modules
+
\ No newline at end of file
diff --git a/doc/Style.md b/doc/Style.md
index c100de1..233888a 100644
--- a/doc/Style.md
+++ b/doc/Style.md
@@ -1,7 +1,9 @@
-# lwtk.Style
+# lwtk.Style Usage
The appearance of gui widgets can be configured for the whole application using
-objects of type *lwtk.Style*.
+objects of type [lwtk.Style].
+
+[lwtk.Style]: ./gen/lwtk/Style.md
*Style* objects contain rule sets for determining the values of named parameters
for a widget according to the widget's class name and the widget's state.
@@ -97,9 +99,8 @@ assert(not ok and err:match("Cannot deduce parameter type"))
Let's define more widget classes:
```lua
-local MyWidget1 = lwtk.newClass("MyWidget1", lwtk.Widget)
local MyWidget2 = lwtk.newClass("MyWidget2", lwtk.Widget)
-local MyWidget3 = lwtk.newClass("MyWidget3", MyWidget2) -- MyWidget3 is derived from MyWidget2
+local MyWidget3 = lwtk.newClass("MyWidget3", MyWidget2) -- MyWidget3 is subclass of MyWidget2
local MyWidget4 = lwtk.newClass("MyWidget4", lwtk.Widget)
```
diff --git a/doc/gen/lwtk/Actionable.md b/doc/gen/lwtk/Actionable.md
new file mode 100644
index 0000000..ca9a3f3
--- /dev/null
+++ b/doc/gen/lwtk/Actionable.md
@@ -0,0 +1,36 @@
+# Mixin lwtk.Actionable
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / _`Actionable`_
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / _`Actionable`_ /
+ * **[FocusHandler](../lwtk/FocusHandler.md#inheritance)**
+ * [Node](../lwtk/Node.md#subclasses) / [Drawable](../lwtk/Drawable.md#subclasses) /
+ * **[Component](../lwtk/Component.md#subclasses)** /
+ * [Compound](../lwtk/Compound.md#subclasses) / **[InnerCompound](../lwtk/InnerCompound.md#inheritance)**
+ * [Styleable](../lwtk/Styleable.md#subclasses) / [Animatable](../lwtk/Animatable.md#subclasses) / **[Widget](../lwtk/Widget.md#subclasses)** / [Compound](../lwtk/Compound.md#subclasses) /
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[TextInput](../lwtk/TextInput.md#inheritance)**
+ * [HotkeyListener](../lwtk/HotkeyListener.md#subclasses) / **[Button](../lwtk/Button.md#subclasses)** /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[PushButton](../lwtk/PushButton.md#inheritance)**
+ * **[TextLabel](../lwtk/TextLabel.md#subclasses)** / **[TitleText](../lwtk/TitleText.md#inheritance)**
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Group](../lwtk/Group.md#subclasses)** /
+ * [Colored](../lwtk/Colored.md#subclasses) / **[ViewSwitcher](../lwtk/ViewSwitcher.md#inheritance)**
+ * **[Column](../lwtk/Column.md#inheritance)**
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) / **[Box](../lwtk/Box.md#subclasses)** / [Focusable](../lwtk/Focusable.md#subclasses) / **[FocusGroup](../lwtk/FocusGroup.md#inheritance)**
+ * **[Matrix](../lwtk/Matrix.md#inheritance)**
+ * **[Row](../lwtk/Row.md#inheritance)**
+ * **[Space](../lwtk/Space.md#inheritance)**
+ * **[Square](../lwtk/Square.md#inheritance)**
+ * **[TextCursor](../lwtk/TextCursor.md#inheritance)**
+ * **[TextFragment](../lwtk/TextFragment.md#inheritance)**
+ * [Styleable](../lwtk/Styleable.md#subclasses) / [KeyHandler](../lwtk/KeyHandler.md#subclasses) / [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Window](../lwtk/Window.md#inheritance)**
+
diff --git a/doc/gen/lwtk/Animatable.md b/doc/gen/lwtk/Animatable.md
new file mode 100644
index 0000000..cc4068a
--- /dev/null
+++ b/doc/gen/lwtk/Animatable.md
@@ -0,0 +1,28 @@
+# Mixin lwtk.Animatable
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / _`Animatable`_
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / [Actionable](../lwtk/Actionable.md#subclasses) / [Node](../lwtk/Node.md#subclasses) / [Drawable](../lwtk/Drawable.md#subclasses) / **[Component](../lwtk/Component.md#subclasses)** / [Styleable](../lwtk/Styleable.md#subclasses) / _`Animatable`_ / **[Widget](../lwtk/Widget.md#subclasses)** / [Compound](../lwtk/Compound.md#subclasses) /
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[TextInput](../lwtk/TextInput.md#inheritance)**
+ * [HotkeyListener](../lwtk/HotkeyListener.md#subclasses) / **[Button](../lwtk/Button.md#subclasses)** /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[PushButton](../lwtk/PushButton.md#inheritance)**
+ * **[TextLabel](../lwtk/TextLabel.md#subclasses)** / **[TitleText](../lwtk/TitleText.md#inheritance)**
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Group](../lwtk/Group.md#subclasses)** /
+ * [Colored](../lwtk/Colored.md#subclasses) / **[ViewSwitcher](../lwtk/ViewSwitcher.md#inheritance)**
+ * **[Column](../lwtk/Column.md#inheritance)**
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) / **[Box](../lwtk/Box.md#subclasses)** / [Focusable](../lwtk/Focusable.md#subclasses) / **[FocusGroup](../lwtk/FocusGroup.md#inheritance)**
+ * **[Matrix](../lwtk/Matrix.md#inheritance)**
+ * **[Row](../lwtk/Row.md#inheritance)**
+ * **[Space](../lwtk/Space.md#inheritance)**
+ * **[Square](../lwtk/Square.md#inheritance)**
+
diff --git a/doc/gen/lwtk/Animations.md b/doc/gen/lwtk/Animations.md
new file mode 100644
index 0000000..596c335
--- /dev/null
+++ b/doc/gen/lwtk/Animations.md
@@ -0,0 +1,32 @@
+# Class lwtk.Animations
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [add()](#.add)
+ * [hasActive()](#.hasActive)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / _**`Animations`**_
+
+## Constructor
+ * **`Animations(app)`**
+
+
+
+## Methods
+ * **`Animations:add(animatable)`**
+
+
+ * **`Animations:hasActive()`**
+
+
+
+## Inherited Methods
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/Application.md b/doc/gen/lwtk/Application.md
new file mode 100644
index 0000000..9064dab
--- /dev/null
+++ b/doc/gen/lwtk/Application.md
@@ -0,0 +1,127 @@
+# Class lwtk.Application
+
+Default application implementation.
+
+Use this for standalone desktop applications. Use [lwtk.love.Application](../lwtk/love/Application.md) for
+running within the [LÖVE](https://love2d.org/) 2D game engine.
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [addStyle()](#.addStyle)
+ * [close()](#.close)
+ * [deferChanges()](#.deferChanges)
+ * [getCurrentTime()](#.getCurrentTime)
+ * [getFontInfo()](#.getFontInfo)
+ * [getLayoutContext()](#.getLayoutContext)
+ * [getScreenScale()](#.getScreenScale)
+ * [hasWindows()](#.hasWindows)
+ * [newWindow()](#.newWindow)
+ * [runEventLoop()](#.runEventLoop) - Update by processing events from the window system.
+ * [setErrorFunc()](#.setErrorFunc)
+ * [setExtensions()](#.setExtensions)
+ * [setStyle()](#.setStyle)
+ * [setTimer()](#.setTimer)
+ * [update()](#.update) - Update by processing events from the window system.
+ * [_addWindow()](#._addWindow)
+ * [_processAllChanges()](#._processAllChanges)
+ * [_removeWindow()](#._removeWindow)
+ * [Inherited Methods](#inherited-methods)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / _**`Application`**_
+
+## Constructor
+ * **`Application(arg1, arg2)`**
+
+
+
+## Methods
+ * **`Application:addStyle(additionalStyle)`**
+
+
+ * **`Application:close()`**
+
+
+ * **`Application:deferChanges(callback)`**
+
+
+ * **`Application:getCurrentTime()`**
+
+
+ * **`Application:getFontInfo(family, slant, weight, size)`**
+
+
+ * **`Application:getLayoutContext()`**
+
+
+ * **`Application:getScreenScale()`**
+
+
+ * **`Application:hasWindows()`**
+
+
+ * **`Application:newWindow(attributes)`**
+
+
+ * **`Application:runEventLoop(timeout)`**
+
+ Update by processing events from the window system.
+
+ * *timeout* - optional float, timeout in seconds
+
+ If *timeout* is given, this function will process events from the window system until
+ the time period in seconds has elapsed or until all window objects have been closed.
+
+ If *timeout* is `nil` or not given, this function will process events from the window system
+ until all window objects have been closed.
+
+ * **`Application:setErrorFunc(...)`**
+
+
+ * **`Application:setExtensions(extensions)`**
+
+
+ * **`Application:setStyle(style)`**
+
+
+ * **`Application:setTimer(seconds, func, ...)`**
+
+
+ * **`Application:update(timeout)`**
+
+ Update by processing events from the window system.
+
+ * *timeout* - optional float, timeout in seconds
+
+ If *timeout* is given, this function will wait for *timeout* seconds until
+ events from the window system become available. If *timeout* is `nil` or not
+ given, this function will block indefinitely until an event occurs.
+
+ As soon as events are available, all events in the queue are processed and this function
+ returns `true`.
+
+ If *timeout* is given and there are no events available after *timeout*
+ seconds, this function will return `false`.
+
+ * **`Application:_addWindow(win)`**
+
+
+ * **`Application:_processAllChanges()`**
+
+
+ * **`Application:_removeWindow(win)`**
+
+
+
+## Inherited Methods
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / _**`Application`**_ / [Node](../lwtk/Node.md#subclasses) / [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[love.Application](../lwtk/love/Application.md#inheritance)**
+
diff --git a/doc/gen/lwtk/Area.md b/doc/gen/lwtk/Area.md
new file mode 100644
index 0000000..3e84aed
--- /dev/null
+++ b/doc/gen/lwtk/Area.md
@@ -0,0 +1,73 @@
+# Class lwtk.Area
+
+A list of rectangle coordinates forming an area.
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [addRect()](#.addRect) - Adds the rectangle coordinates to the area.
+ * [clear()](#.clear) - Clears all rectangle coordinates.
+ * [getRect()](#.getRect) - Obtain coordinates of the i-th rectangle from the area.
+ * [intersects()](#.intersects) - Returns *true* if the given rectangle coordinates intersect the area.
+ * [intersectsBorder()](#.intersectsBorder)
+ * [isWithin()](#.isWithin) - Returns *true* if the given rectangle coordinates are within the area.
+ * [iteration()](#.iteration) - Iterate through all rectangle coordinates.
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / _**`Area`**_
+
+## Constructor
+ * **`Area()`**
+
+ Creates an empty area object.
+
+
+## Methods
+ * **`Area:addRect(x, y, w, h)`**
+
+ Adds the rectangle coordinates to the area.
+
+ * **`Area:clear()`**
+
+ Clears all rectangle coordinates. After this the
+ area does not contain any rectangle, i.e. *area.count == 0*.
+
+ * **`Area:getRect(i)`**
+
+ Obtain coordinates of the i-th rectangle from the area.
+
+ * *i* - index of the rectangle, *1 <= i <= area.count*
+
+ Returns *x, y, w, h* rectangle coordinates
+
+ * **`Area:intersects(x, y, w, h)`**
+
+ Returns *true* if the given rectangle coordinates intersect
+ the area.
+
+ * **`Area:intersectsBorder(x, y, w, h, borderWidth)`**
+
+
+ * **`Area:isWithin(x, y, w, h)`**
+
+ Returns *true* if the given rectangle coordinates are
+ within the area.
+
+ * **`Area:iteration()`**
+
+ Iterate through all rectangle coordinates.
+
+ Returns an *iterator function*, *self* and *0*, so that the construction
+ ```lua
+ for i, x, y, w, h in area:iteration() do ... end
+ ```
+ will iterate over all rectangle indices and coordinates.
+
+
+## Inherited Methods
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/Box.md b/doc/gen/lwtk/Box.md
new file mode 100644
index 0000000..49045a5
--- /dev/null
+++ b/doc/gen/lwtk/Box.md
@@ -0,0 +1,57 @@
+# Class lwtk.Box
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [getMeasures()](#.getMeasures)
+ * [Inherited Methods](#inherited-methods)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) / [MouseDispatcher](../lwtk/MouseDispatcher.md#inheritance) / **[Group](../lwtk/Group.md#inheritance)** / [LayoutFrame](../lwtk/LayoutFrame.md#inheritance) / [Control](../lwtk/Control.md#inheritance) / _**`Box`**_
+
+## Constructor
+ * **`Box(initParams)`**
+
+ * Inherited from: **[Group()](../lwtk/Group.md#constructor)**
+
+
+## Methods
+ * **`Box:getMeasures()`**
+
+ * Implementation: [LayoutFrame:extra.getMeasures()](../lwtk/LayoutFrame.md#extra.getMeasures)
+ * Implements: [Component:getMeasures()](../lwtk/Component.md#.getMeasures)
+
+
+
+## Inherited Methods
+ * [LayoutFrame](../lwtk/LayoutFrame.md):
+ * [addChild()](../lwtk/LayoutFrame.md#.addChild), [onDraw()](../lwtk/LayoutFrame.md#.onDraw), [onLayout()](../lwtk/LayoutFrame.md#.onLayout)
+ * **[Group](../lwtk/Group.md)**:
+ * [childById()](../lwtk/Group.md#.childById), [_clearChildLookup()](../lwtk/Group.md#._clearChildLookup)
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md):
+ * [_processMouseDown()](../lwtk/MouseDispatcher.md#._processMouseDown), [_processMouseEnter()](../lwtk/MouseDispatcher.md#._processMouseEnter), [_processMouseLeave()](../lwtk/MouseDispatcher.md#._processMouseLeave), [_processMouseMove()](../lwtk/MouseDispatcher.md#._processMouseMove), [_processMouseScroll()](../lwtk/MouseDispatcher.md#._processMouseScroll), [_processMouseUp()](../lwtk/MouseDispatcher.md#._processMouseUp)
+ * [Compound](../lwtk/Compound.md):
+ * [_processChanges()](../lwtk/Compound.md#._processChanges), [_processDraw()](../lwtk/Compound.md#._processDraw)
+ * **[Widget](../lwtk/Widget.md)**:
+ * [notifyInputChanged()](../lwtk/Widget.md#.notifyInputChanged), [setOnInputChanged()](../lwtk/Widget.md#.setOnInputChanged), [setOnRealize()](../lwtk/Widget.md#.setOnRealize), [_setApp()](../lwtk/Widget.md#._setApp), [_setParent()](../lwtk/Widget.md#._setParent)
+ * [Animatable](../lwtk/Animatable.md):
+ * [animateFrame()](../lwtk/Animatable.md#.animateFrame), [getStyle()](../lwtk/Animatable.md#.getStyle), [getStyleParam()](../lwtk/Animatable.md#.getStyleParam), [isVisible()](../lwtk/Animatable.md#.isVisible), [setState()](../lwtk/Animatable.md#.setState), [setStates()](../lwtk/Animatable.md#.setStates), [setStyle()](../lwtk/Animatable.md#.setStyle), [setVisible()](../lwtk/Animatable.md#.setVisible), [updateAnimation()](../lwtk/Animatable.md#.updateAnimation), [_setStyleFromParent()](../lwtk/Animatable.md#._setStyleFromParent)
+ * [Styleable](../lwtk/Styleable.md):
+ * [clearStyleCache()](../lwtk/Styleable.md#.clearStyleCache), [getStateString()](../lwtk/Styleable.md#.getStateString), [_getStyleParam()](../lwtk/Styleable.md#._getStyleParam)
+ * **[Component](../lwtk/Component.md)**:
+ * [byId()](../lwtk/Component.md#.byId), [getCurrentTime()](../lwtk/Component.md#.getCurrentTime), [getFocusHandler()](../lwtk/Component.md#.getFocusHandler), [getFontInfo()](../lwtk/Component.md#.getFontInfo), [getFrame()](../lwtk/Component.md#.getFrame), [getLayoutContext()](../lwtk/Component.md#.getLayoutContext), [getParent()](../lwtk/Component.md#.getParent), [getRoot()](../lwtk/Component.md#.getRoot), [getSize()](../lwtk/Component.md#.getSize), [handleRemainingInitParams()](../lwtk/Component.md#.handleRemainingInitParams), [parentById()](../lwtk/Component.md#.parentById), [setFrame()](../lwtk/Component.md#.setFrame), [setInitParams()](../lwtk/Component.md#.setInitParams), [setTimer()](../lwtk/Component.md#.setTimer), [transformXY()](../lwtk/Component.md#.transformXY), [triggerLayout()](../lwtk/Component.md#.triggerLayout), [triggerRedraw()](../lwtk/Component.md#.triggerRedraw), [updateFrameTransition()](../lwtk/Component.md#.updateFrameTransition), [_setFrame()](../lwtk/Component.md#._setFrame)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / [Actionable](../lwtk/Actionable.md#subclasses) / [Node](../lwtk/Node.md#subclasses) / [Drawable](../lwtk/Drawable.md#subclasses) / **[Component](../lwtk/Component.md#subclasses)** / [Styleable](../lwtk/Styleable.md#subclasses) / [Animatable](../lwtk/Animatable.md#subclasses) / **[Widget](../lwtk/Widget.md#subclasses)** / [Compound](../lwtk/Compound.md#subclasses) / [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Group](../lwtk/Group.md#subclasses)** / [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) / _**`Box`**_ / [Focusable](../lwtk/Focusable.md#subclasses) / **[FocusGroup](../lwtk/FocusGroup.md#inheritance)**
+
diff --git a/doc/gen/lwtk/BuiltinStyleTypes.md b/doc/gen/lwtk/BuiltinStyleTypes.md
new file mode 100644
index 0000000..e5a2e53
--- /dev/null
+++ b/doc/gen/lwtk/BuiltinStyleTypes.md
@@ -0,0 +1,5 @@
+# Function lwtk.BuiltinStyleTypes
+
+ * **`BuiltinStyleTypes()`**
+
+
diff --git a/doc/gen/lwtk/Button.md b/doc/gen/lwtk/Button.md
new file mode 100644
index 0000000..84e0229
--- /dev/null
+++ b/doc/gen/lwtk/Button.md
@@ -0,0 +1,47 @@
+# Class lwtk.Button
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Inherited Methods](#inherited-methods)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) / [LayoutFrame](../lwtk/LayoutFrame.md#inheritance) / [Control](../lwtk/Control.md#inheritance) / [HotkeyListener](../lwtk/HotkeyListener.md#inheritance) / _**`Button`**_
+
+## Constructor
+ * **`Button(initParams)`**
+
+ * Inherited from: **[Widget()](../lwtk/Widget.md#constructor)**
+
+
+## Inherited Methods
+ * [HotkeyListener](../lwtk/HotkeyListener.md):
+ * [isHotkeyEnabled()](../lwtk/HotkeyListener.md#.isHotkeyEnabled), [onDisabled()](../lwtk/HotkeyListener.md#.onDisabled), [onEffectiveVisibilityChanged()](../lwtk/HotkeyListener.md#.onEffectiveVisibilityChanged), [onHotkeyDisabled()](../lwtk/HotkeyListener.md#.onHotkeyDisabled), [onHotkeyEnabled()](../lwtk/HotkeyListener.md#.onHotkeyEnabled), [setHotkey()](../lwtk/HotkeyListener.md#.setHotkey), [_handleHasFocusHandler()](../lwtk/HotkeyListener.md#._handleHasFocusHandler)
+ * [LayoutFrame](../lwtk/LayoutFrame.md):
+ * [addChild()](../lwtk/LayoutFrame.md#.addChild), [onDraw()](../lwtk/LayoutFrame.md#.onDraw), [onLayout()](../lwtk/LayoutFrame.md#.onLayout)
+ * [Compound](../lwtk/Compound.md):
+ * [_processChanges()](../lwtk/Compound.md#._processChanges), [_processDraw()](../lwtk/Compound.md#._processDraw)
+ * **[Widget](../lwtk/Widget.md)**:
+ * [notifyInputChanged()](../lwtk/Widget.md#.notifyInputChanged), [setOnInputChanged()](../lwtk/Widget.md#.setOnInputChanged), [setOnRealize()](../lwtk/Widget.md#.setOnRealize), [_setApp()](../lwtk/Widget.md#._setApp), [_setParent()](../lwtk/Widget.md#._setParent)
+ * [Animatable](../lwtk/Animatable.md):
+ * [animateFrame()](../lwtk/Animatable.md#.animateFrame), [getStyle()](../lwtk/Animatable.md#.getStyle), [getStyleParam()](../lwtk/Animatable.md#.getStyleParam), [isVisible()](../lwtk/Animatable.md#.isVisible), [setState()](../lwtk/Animatable.md#.setState), [setStates()](../lwtk/Animatable.md#.setStates), [setStyle()](../lwtk/Animatable.md#.setStyle), [setVisible()](../lwtk/Animatable.md#.setVisible), [updateAnimation()](../lwtk/Animatable.md#.updateAnimation), [_setStyleFromParent()](../lwtk/Animatable.md#._setStyleFromParent)
+ * [Styleable](../lwtk/Styleable.md):
+ * [clearStyleCache()](../lwtk/Styleable.md#.clearStyleCache), [getStateString()](../lwtk/Styleable.md#.getStateString), [_getStyleParam()](../lwtk/Styleable.md#._getStyleParam)
+ * **[Component](../lwtk/Component.md)**:
+ * [byId()](../lwtk/Component.md#.byId), [getCurrentTime()](../lwtk/Component.md#.getCurrentTime), [getFocusHandler()](../lwtk/Component.md#.getFocusHandler), [getFontInfo()](../lwtk/Component.md#.getFontInfo), [getFrame()](../lwtk/Component.md#.getFrame), [getLayoutContext()](../lwtk/Component.md#.getLayoutContext), [getParent()](../lwtk/Component.md#.getParent), [getRoot()](../lwtk/Component.md#.getRoot), [getSize()](../lwtk/Component.md#.getSize), [handleRemainingInitParams()](../lwtk/Component.md#.handleRemainingInitParams), [parentById()](../lwtk/Component.md#.parentById), [setFrame()](../lwtk/Component.md#.setFrame), [setInitParams()](../lwtk/Component.md#.setInitParams), [setTimer()](../lwtk/Component.md#.setTimer), [transformXY()](../lwtk/Component.md#.transformXY), [triggerLayout()](../lwtk/Component.md#.triggerLayout), [triggerRedraw()](../lwtk/Component.md#.triggerRedraw), [updateFrameTransition()](../lwtk/Component.md#.updateFrameTransition), [_setFrame()](../lwtk/Component.md#._setFrame)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam), [_processMouseDown()](../lwtk/Drawable.md#._processMouseDown), [_processMouseEnter()](../lwtk/Drawable.md#._processMouseEnter), [_processMouseLeave()](../lwtk/Drawable.md#._processMouseLeave), [_processMouseMove()](../lwtk/Drawable.md#._processMouseMove), [_processMouseScroll()](../lwtk/Drawable.md#._processMouseScroll), [_processMouseUp()](../lwtk/Drawable.md#._processMouseUp)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / [Actionable](../lwtk/Actionable.md#subclasses) / [Node](../lwtk/Node.md#subclasses) / [Drawable](../lwtk/Drawable.md#subclasses) / **[Component](../lwtk/Component.md#subclasses)** / [Styleable](../lwtk/Styleable.md#subclasses) / [Animatable](../lwtk/Animatable.md#subclasses) / **[Widget](../lwtk/Widget.md#subclasses)** / [Compound](../lwtk/Compound.md#subclasses) / [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) / [HotkeyListener](../lwtk/HotkeyListener.md#subclasses) / _**`Button`**_ /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[PushButton](../lwtk/PushButton.md#inheritance)**
+ * **[TextLabel](../lwtk/TextLabel.md#subclasses)** / **[TitleText](../lwtk/TitleText.md#inheritance)**
+
diff --git a/doc/gen/lwtk/Callback.md b/doc/gen/lwtk/Callback.md
new file mode 100644
index 0000000..ab31502
--- /dev/null
+++ b/doc/gen/lwtk/Callback.md
@@ -0,0 +1,14 @@
+# Meta lwtk.Callback
+
+Holds a function with arguments that can
+be called.
+
+## Contents
+
+ * [Constructor](#constructor)
+
+
+## Constructor
+ * **`Callback(func, ...)`**
+
+
diff --git a/doc/gen/lwtk/ChildLookup.md b/doc/gen/lwtk/ChildLookup.md
new file mode 100644
index 0000000..46b0ff2
--- /dev/null
+++ b/doc/gen/lwtk/ChildLookup.md
@@ -0,0 +1,12 @@
+# Meta lwtk.ChildLookup
+
+
+## Contents
+
+ * [Constructor](#constructor)
+
+
+## Constructor
+ * **`ChildLookup(group)`**
+
+
diff --git a/doc/gen/lwtk/Class.md b/doc/gen/lwtk/Class.md
new file mode 100644
index 0000000..4d3a606
--- /dev/null
+++ b/doc/gen/lwtk/Class.md
@@ -0,0 +1,7 @@
+# Table lwtk.Class
+
+Metatable for objects created by [lwtk.newClass](../lwtk/newClass.md)().
+
+See [lwtk.Class Usage](../../Class.md) for detailed documentation
+and usage examples.
+
diff --git a/doc/gen/lwtk/Color.md b/doc/gen/lwtk/Color.md
new file mode 100644
index 0000000..5da7e6c
--- /dev/null
+++ b/doc/gen/lwtk/Color.md
@@ -0,0 +1,74 @@
+# Class lwtk.Color
+
+RGBA Color value.
+
+Contains the float values *r (red)*, *g (green)*, *b (blue)* and optional
+*a (alpha value)* in the range >= 0 and <= 1.
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [lighten()](#.lighten)
+ * [saturate()](#.saturate)
+ * [toHex()](#.toHex)
+ * [toRGB()](#.toRGB)
+ * [toRGBA()](#.toRGBA)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / _**`Color`**_
+
+## Constructor
+ * **`Color(...)`**
+
+ Creates a new RGBA color value.
+
+ Possible invocations:
+
+ * **`Color()`**
+
+ If called without arguments, the color black is created, i.e.
+ *r = g = b = 0*.
+
+ * **`Color(r,g,b,a)`**
+
+ If more than one arg is given, arguments are the float color
+ values *r*, *g*, *b*, *a* with the alpha value *a* being optional.
+
+ * **`Color(table)`**
+
+ * *table* - a table with the entries for the keys *r*, *g*, *b*
+ with optional alpha value *a*.
+ Missing entries for *r*, *g*, *b* are treated as 0.
+
+ * **`Color(string)`**
+
+ * *string* - a string in hex encoding with length 6 or 8
+ (format *"rrggbb"* or *"rrggbbaa"*). Each color value
+ consists of two hexadecimal digits and is in the range
+ *00 <= value <= ff*, alpha value is optional.
+
+
+## Methods
+ * **`Color:lighten(amount)`**
+
+
+ * **`Color:saturate(amount)`**
+
+
+ * **`Color:toHex()`**
+
+
+ * **`Color:toRGB()`**
+
+
+ * **`Color:toRGBA(a)`**
+
+
+
+## Inherited Methods
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/Colored.md b/doc/gen/lwtk/Colored.md
new file mode 100644
index 0000000..4ade7b7
--- /dev/null
+++ b/doc/gen/lwtk/Colored.md
@@ -0,0 +1,15 @@
+# Mixin lwtk.Colored
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) / [MouseDispatcher](../lwtk/MouseDispatcher.md#inheritance) / **[Group](../lwtk/Group.md#inheritance)** / _`Colored`_
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / [Actionable](../lwtk/Actionable.md#subclasses) / [Node](../lwtk/Node.md#subclasses) / [Drawable](../lwtk/Drawable.md#subclasses) / **[Component](../lwtk/Component.md#subclasses)** / [Styleable](../lwtk/Styleable.md#subclasses) / [Animatable](../lwtk/Animatable.md#subclasses) / **[Widget](../lwtk/Widget.md#subclasses)** / [Compound](../lwtk/Compound.md#subclasses) / [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Group](../lwtk/Group.md#subclasses)** / _`Colored`_ / **[ViewSwitcher](../lwtk/ViewSwitcher.md#inheritance)**
+
diff --git a/doc/gen/lwtk/Column.md b/doc/gen/lwtk/Column.md
new file mode 100644
index 0000000..e45bd3a
--- /dev/null
+++ b/doc/gen/lwtk/Column.md
@@ -0,0 +1,55 @@
+# Class lwtk.Column
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [getMeasures()](#.getMeasures)
+ * [onLayout()](#.onLayout)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) / [MouseDispatcher](../lwtk/MouseDispatcher.md#inheritance) / **[Group](../lwtk/Group.md#inheritance)** / _**`Column`**_
+
+## Constructor
+ * **`Column(initParams)`**
+
+ * Inherited from: **[Group()](../lwtk/Group.md#constructor)**
+
+
+## Methods
+ * **`Column:getMeasures()`**
+
+ * Implements: [Component:getMeasures()](../lwtk/Component.md#.getMeasures)
+
+
+ * **`Column:onLayout(width, height, isLayoutTransition)`**
+
+ * Implements: [Component:onLayout()](../lwtk/Component.md#.onLayout)
+
+
+
+## Inherited Methods
+ * **[Group](../lwtk/Group.md)**:
+ * [addChild()](../lwtk/Group.md#.addChild), [childById()](../lwtk/Group.md#.childById), [_clearChildLookup()](../lwtk/Group.md#._clearChildLookup)
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md):
+ * [_processMouseDown()](../lwtk/MouseDispatcher.md#._processMouseDown), [_processMouseEnter()](../lwtk/MouseDispatcher.md#._processMouseEnter), [_processMouseLeave()](../lwtk/MouseDispatcher.md#._processMouseLeave), [_processMouseMove()](../lwtk/MouseDispatcher.md#._processMouseMove), [_processMouseScroll()](../lwtk/MouseDispatcher.md#._processMouseScroll), [_processMouseUp()](../lwtk/MouseDispatcher.md#._processMouseUp)
+ * [Compound](../lwtk/Compound.md):
+ * [_processChanges()](../lwtk/Compound.md#._processChanges), [_processDraw()](../lwtk/Compound.md#._processDraw)
+ * **[Widget](../lwtk/Widget.md)**:
+ * [notifyInputChanged()](../lwtk/Widget.md#.notifyInputChanged), [setOnInputChanged()](../lwtk/Widget.md#.setOnInputChanged), [setOnRealize()](../lwtk/Widget.md#.setOnRealize), [_setApp()](../lwtk/Widget.md#._setApp), [_setParent()](../lwtk/Widget.md#._setParent)
+ * [Animatable](../lwtk/Animatable.md):
+ * [animateFrame()](../lwtk/Animatable.md#.animateFrame), [getStyle()](../lwtk/Animatable.md#.getStyle), [getStyleParam()](../lwtk/Animatable.md#.getStyleParam), [isVisible()](../lwtk/Animatable.md#.isVisible), [setState()](../lwtk/Animatable.md#.setState), [setStates()](../lwtk/Animatable.md#.setStates), [setStyle()](../lwtk/Animatable.md#.setStyle), [setVisible()](../lwtk/Animatable.md#.setVisible), [updateAnimation()](../lwtk/Animatable.md#.updateAnimation), [_setStyleFromParent()](../lwtk/Animatable.md#._setStyleFromParent)
+ * [Styleable](../lwtk/Styleable.md):
+ * [clearStyleCache()](../lwtk/Styleable.md#.clearStyleCache), [getStateString()](../lwtk/Styleable.md#.getStateString), [_getStyleParam()](../lwtk/Styleable.md#._getStyleParam)
+ * **[Component](../lwtk/Component.md)**:
+ * [byId()](../lwtk/Component.md#.byId), [getCurrentTime()](../lwtk/Component.md#.getCurrentTime), [getFocusHandler()](../lwtk/Component.md#.getFocusHandler), [getFontInfo()](../lwtk/Component.md#.getFontInfo), [getFrame()](../lwtk/Component.md#.getFrame), [getLayoutContext()](../lwtk/Component.md#.getLayoutContext), [getParent()](../lwtk/Component.md#.getParent), [getRoot()](../lwtk/Component.md#.getRoot), [getSize()](../lwtk/Component.md#.getSize), [handleRemainingInitParams()](../lwtk/Component.md#.handleRemainingInitParams), [parentById()](../lwtk/Component.md#.parentById), [setFrame()](../lwtk/Component.md#.setFrame), [setInitParams()](../lwtk/Component.md#.setInitParams), [setTimer()](../lwtk/Component.md#.setTimer), [transformXY()](../lwtk/Component.md#.transformXY), [triggerLayout()](../lwtk/Component.md#.triggerLayout), [triggerRedraw()](../lwtk/Component.md#.triggerRedraw), [updateFrameTransition()](../lwtk/Component.md#.updateFrameTransition), [_setFrame()](../lwtk/Component.md#._setFrame)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/Component.md b/doc/gen/lwtk/Component.md
new file mode 100644
index 0000000..f8764aa
--- /dev/null
+++ b/doc/gen/lwtk/Component.md
@@ -0,0 +1,156 @@
+# Class lwtk.Component
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [animateFrame()](#.animateFrame)
+ * [byId()](#.byId)
+ * [getCurrentTime()](#.getCurrentTime)
+ * [getFocusHandler()](#.getFocusHandler)
+ * [getFontInfo()](#.getFontInfo)
+ * [getFrame()](#.getFrame)
+ * [getLayoutContext()](#.getLayoutContext)
+ * [getParent()](#.getParent)
+ * [getRoot()](#.getRoot)
+ * [getSize()](#.getSize)
+ * [handleRemainingInitParams()](#.handleRemainingInitParams)
+ * [parentById()](#.parentById)
+ * [setFrame()](#.setFrame)
+ * [setInitParams()](#.setInitParams)
+ * [setTimer()](#.setTimer)
+ * [transformXY()](#.transformXY)
+ * [triggerLayout()](#.triggerLayout)
+ * [triggerRedraw()](#.triggerRedraw)
+ * [updateAnimation()](#.updateAnimation)
+ * [updateFrameTransition()](#.updateFrameTransition)
+ * [_processChanges()](#._processChanges)
+ * [_processDraw()](#._processDraw)
+ * [_setApp()](#._setApp)
+ * [_setFrame()](#._setFrame)
+ * [_setParent()](#._setParent)
+ * [Inherited Methods](#inherited-methods)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / _**`Component`**_
+
+## Constructor
+ * **`Component(initParams)`**
+
+ * Overrides: [Actionable()](../lwtk/Actionable.md#constructor)
+
+
+
+## Methods
+ * **`Component:animateFrame(...)`**
+
+
+ * **`Component:byId(id)`**
+
+
+ * **`Component:getCurrentTime()`**
+
+
+ * **`Component:getFocusHandler()`**
+
+
+ * **`Component:getFontInfo(family, slant, weight, size)`**
+
+
+ * **`Component:getFrame()`**
+
+
+ * **`Component:getLayoutContext()`**
+
+
+ * **`Component:getParent()`**
+
+
+ * **`Component:getRoot()`**
+
+
+ * **`Component:getSize()`**
+
+
+ * **`Component:handleRemainingInitParams(initParams)`**
+
+ * Overrides: [Actionable:handleRemainingInitParams()](../lwtk/Actionable.md#.handleRemainingInitParams)
+
+
+ * **`Component:parentById(id)`**
+
+
+ * **`Component:setFrame(...)`**
+
+
+ * **`Component:setInitParams(initParams)`**
+
+ * Overrides: [Actionable:setInitParams()](../lwtk/Actionable.md#.setInitParams)
+
+
+ * **`Component:setTimer(seconds, func, ...)`**
+
+
+ * **`Component:transformXY(x, y, parent)`**
+
+
+ * **`Component:triggerLayout()`**
+
+
+ * **`Component:triggerRedraw()`**
+
+
+ * **`Component:updateAnimation()`**
+
+
+ * **`Component:updateFrameTransition()`**
+
+
+ * **`Component:_processChanges(x0, y0, cx, cy, cw, ch, damagedArea)`**
+
+
+ * **`Component:_processDraw(ctx, x0, y0, cx, cy, cw, ch, exposedArea)`**
+
+
+ * **`Component:_setApp(app)`**
+
+
+ * **`Component:_setFrame(newX, newY, newW, newH, fromFrameAnimation)`**
+
+
+ * **`Component:_setParent(parent)`**
+
+
+
+## Inherited Methods
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam), [getStyleParam()](../lwtk/Drawable.md#.getStyleParam), [_processMouseDown()](../lwtk/Drawable.md#._processMouseDown), [_processMouseEnter()](../lwtk/Drawable.md#._processMouseEnter), [_processMouseLeave()](../lwtk/Drawable.md#._processMouseLeave), [_processMouseMove()](../lwtk/Drawable.md#._processMouseMove), [_processMouseScroll()](../lwtk/Drawable.md#._processMouseScroll), [_processMouseUp()](../lwtk/Drawable.md#._processMouseUp)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / [Actionable](../lwtk/Actionable.md#subclasses) / [Node](../lwtk/Node.md#subclasses) / [Drawable](../lwtk/Drawable.md#subclasses) / _**`Component`**_ /
+ * [Compound](../lwtk/Compound.md#subclasses) / **[InnerCompound](../lwtk/InnerCompound.md#inheritance)**
+ * [Styleable](../lwtk/Styleable.md#subclasses) / [Animatable](../lwtk/Animatable.md#subclasses) / **[Widget](../lwtk/Widget.md#subclasses)** / [Compound](../lwtk/Compound.md#subclasses) /
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[TextInput](../lwtk/TextInput.md#inheritance)**
+ * [HotkeyListener](../lwtk/HotkeyListener.md#subclasses) / **[Button](../lwtk/Button.md#subclasses)** /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[PushButton](../lwtk/PushButton.md#inheritance)**
+ * **[TextLabel](../lwtk/TextLabel.md#subclasses)** / **[TitleText](../lwtk/TitleText.md#inheritance)**
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Group](../lwtk/Group.md#subclasses)** /
+ * [Colored](../lwtk/Colored.md#subclasses) / **[ViewSwitcher](../lwtk/ViewSwitcher.md#inheritance)**
+ * **[Column](../lwtk/Column.md#inheritance)**
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) / **[Box](../lwtk/Box.md#subclasses)** / [Focusable](../lwtk/Focusable.md#subclasses) / **[FocusGroup](../lwtk/FocusGroup.md#inheritance)**
+ * **[Matrix](../lwtk/Matrix.md#inheritance)**
+ * **[Row](../lwtk/Row.md#inheritance)**
+ * **[Space](../lwtk/Space.md#inheritance)**
+ * **[Square](../lwtk/Square.md#inheritance)**
+ * **[TextCursor](../lwtk/TextCursor.md#inheritance)**
+ * **[TextFragment](../lwtk/TextFragment.md#inheritance)**
+
diff --git a/doc/gen/lwtk/Compound.md b/doc/gen/lwtk/Compound.md
new file mode 100644
index 0000000..741fb20
--- /dev/null
+++ b/doc/gen/lwtk/Compound.md
@@ -0,0 +1,32 @@
+# Mixin lwtk.Compound
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** /
+ * _`Compound`_
+ * [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / _`Compound`_
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / [Actionable](../lwtk/Actionable.md#subclasses) / [Node](../lwtk/Node.md#subclasses) / [Drawable](../lwtk/Drawable.md#subclasses) / **[Component](../lwtk/Component.md#subclasses)** /
+ * _`Compound`_ / **[InnerCompound](../lwtk/InnerCompound.md#inheritance)**
+ * [Styleable](../lwtk/Styleable.md#subclasses) / [Animatable](../lwtk/Animatable.md#subclasses) / **[Widget](../lwtk/Widget.md#subclasses)** / _`Compound`_ /
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[TextInput](../lwtk/TextInput.md#inheritance)**
+ * [HotkeyListener](../lwtk/HotkeyListener.md#subclasses) / **[Button](../lwtk/Button.md#subclasses)** /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[PushButton](../lwtk/PushButton.md#inheritance)**
+ * **[TextLabel](../lwtk/TextLabel.md#subclasses)** / **[TitleText](../lwtk/TitleText.md#inheritance)**
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Group](../lwtk/Group.md#subclasses)** /
+ * [Colored](../lwtk/Colored.md#subclasses) / **[ViewSwitcher](../lwtk/ViewSwitcher.md#inheritance)**
+ * **[Column](../lwtk/Column.md#inheritance)**
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) / **[Box](../lwtk/Box.md#subclasses)** / [Focusable](../lwtk/Focusable.md#subclasses) / **[FocusGroup](../lwtk/FocusGroup.md#inheritance)**
+ * **[Matrix](../lwtk/Matrix.md#inheritance)**
+ * **[Row](../lwtk/Row.md#inheritance)**
+ * **[Space](../lwtk/Space.md#inheritance)**
+ * **[Square](../lwtk/Square.md#inheritance)**
+
diff --git a/doc/gen/lwtk/Control.md b/doc/gen/lwtk/Control.md
new file mode 100644
index 0000000..674280d
--- /dev/null
+++ b/doc/gen/lwtk/Control.md
@@ -0,0 +1,23 @@
+# Mixin lwtk.Control
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) /
+ * [LayoutFrame](../lwtk/LayoutFrame.md#inheritance) / _`Control`_
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md#inheritance) / **[Group](../lwtk/Group.md#inheritance)** / [LayoutFrame](../lwtk/LayoutFrame.md#inheritance) / _`Control`_
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / [Actionable](../lwtk/Actionable.md#subclasses) / [Node](../lwtk/Node.md#subclasses) / [Drawable](../lwtk/Drawable.md#subclasses) / **[Component](../lwtk/Component.md#subclasses)** / [Styleable](../lwtk/Styleable.md#subclasses) / [Animatable](../lwtk/Animatable.md#subclasses) / **[Widget](../lwtk/Widget.md#subclasses)** / [Compound](../lwtk/Compound.md#subclasses) /
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / _`Control`_ /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[TextInput](../lwtk/TextInput.md#inheritance)**
+ * [HotkeyListener](../lwtk/HotkeyListener.md#subclasses) / **[Button](../lwtk/Button.md#subclasses)** /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[PushButton](../lwtk/PushButton.md#inheritance)**
+ * **[TextLabel](../lwtk/TextLabel.md#subclasses)** / **[TitleText](../lwtk/TitleText.md#inheritance)**
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Group](../lwtk/Group.md#subclasses)** / [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / _`Control`_ / **[Box](../lwtk/Box.md#subclasses)** / [Focusable](../lwtk/Focusable.md#subclasses) / **[FocusGroup](../lwtk/FocusGroup.md#inheritance)**
+
diff --git a/doc/gen/lwtk/DefaultKeyBinding.md b/doc/gen/lwtk/DefaultKeyBinding.md
new file mode 100644
index 0000000..8e79c7b
--- /dev/null
+++ b/doc/gen/lwtk/DefaultKeyBinding.md
@@ -0,0 +1,5 @@
+# Function lwtk.DefaultKeyBinding
+
+ * **`DefaultKeyBinding()`**
+
+ Returns new [lwtk.KeyBinding](../lwtk/KeyBinding.md) object with default settings.
diff --git a/doc/gen/lwtk/DefaultStyle.md b/doc/gen/lwtk/DefaultStyle.md
new file mode 100644
index 0000000..96ed246
--- /dev/null
+++ b/doc/gen/lwtk/DefaultStyle.md
@@ -0,0 +1,25 @@
+# Class lwtk.DefaultStyle
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / **[Style](../lwtk/Style.md#inheritance)** / _**`DefaultStyle`**_
+
+## Constructor
+ * **`DefaultStyle(initParams)`**
+
+ * Overrides: **[Style()](../lwtk/Style.md#constructor)**
+
+
+
+## Inherited Methods
+ * **[Style](../lwtk/Style.md)**:
+ * [addRules()](../lwtk/Style.md#.addRules), [clearCache()](../lwtk/Style.md#.clearCache), [getScaleFactor()](../lwtk/Style.md#.getScaleFactor), [getStyleParam()](../lwtk/Style.md#.getStyleParam), [isAnimatable()](../lwtk/Style.md#.isAnimatable), [isScalable()](../lwtk/Style.md#.isScalable), [setRules()](../lwtk/Style.md#.setRules), [setScaleFactor()](../lwtk/Style.md#.setScaleFactor), [_getStyleParam()](../lwtk/Style.md#._getStyleParam), [_getStyleParam2()](../lwtk/Style.md#._getStyleParam2), [_replaceParentStyle()](../lwtk/Style.md#._replaceParentStyle), [_setParentStyle()](../lwtk/Style.md#._setParentStyle)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/Drawable.md b/doc/gen/lwtk/Drawable.md
new file mode 100644
index 0000000..2118085
--- /dev/null
+++ b/doc/gen/lwtk/Drawable.md
@@ -0,0 +1,34 @@
+# Mixin lwtk.Drawable
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / _`Drawable`_
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / [Actionable](../lwtk/Actionable.md#subclasses) / [Node](../lwtk/Node.md#subclasses) / _`Drawable`_ /
+ * **[Component](../lwtk/Component.md#subclasses)** /
+ * [Compound](../lwtk/Compound.md#subclasses) / **[InnerCompound](../lwtk/InnerCompound.md#inheritance)**
+ * [Styleable](../lwtk/Styleable.md#subclasses) / [Animatable](../lwtk/Animatable.md#subclasses) / **[Widget](../lwtk/Widget.md#subclasses)** / [Compound](../lwtk/Compound.md#subclasses) /
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[TextInput](../lwtk/TextInput.md#inheritance)**
+ * [HotkeyListener](../lwtk/HotkeyListener.md#subclasses) / **[Button](../lwtk/Button.md#subclasses)** /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[PushButton](../lwtk/PushButton.md#inheritance)**
+ * **[TextLabel](../lwtk/TextLabel.md#subclasses)** / **[TitleText](../lwtk/TitleText.md#inheritance)**
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Group](../lwtk/Group.md#subclasses)** /
+ * [Colored](../lwtk/Colored.md#subclasses) / **[ViewSwitcher](../lwtk/ViewSwitcher.md#inheritance)**
+ * **[Column](../lwtk/Column.md#inheritance)**
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) / **[Box](../lwtk/Box.md#subclasses)** / [Focusable](../lwtk/Focusable.md#subclasses) / **[FocusGroup](../lwtk/FocusGroup.md#inheritance)**
+ * **[Matrix](../lwtk/Matrix.md#inheritance)**
+ * **[Row](../lwtk/Row.md#inheritance)**
+ * **[Space](../lwtk/Space.md#inheritance)**
+ * **[Square](../lwtk/Square.md#inheritance)**
+ * **[TextCursor](../lwtk/TextCursor.md#inheritance)**
+ * **[TextFragment](../lwtk/TextFragment.md#inheritance)**
+ * [Styleable](../lwtk/Styleable.md#subclasses) / [KeyHandler](../lwtk/KeyHandler.md#subclasses) / [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Window](../lwtk/Window.md#inheritance)**
+
diff --git a/doc/gen/lwtk/FocusGroup.md b/doc/gen/lwtk/FocusGroup.md
new file mode 100644
index 0000000..9c9e449
--- /dev/null
+++ b/doc/gen/lwtk/FocusGroup.md
@@ -0,0 +1,122 @@
+# Class lwtk.FocusGroup
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [handleHotkey()](#.handleHotkey)
+ * [invokeActionMethod()](#.invokeActionMethod)
+ * [onActionEnterFocusGroup()](#.onActionEnterFocusGroup)
+ * [onActionExitFocusGroup()](#.onActionExitFocusGroup)
+ * [onKeyDown()](#.onKeyDown)
+ * [setFocus()](#.setFocus)
+ * [_handleChildRequestsFocus()](#._handleChildRequestsFocus)
+ * [_handleFocusIn()](#._handleFocusIn)
+ * [_handleFocusOut()](#._handleFocusOut)
+ * [_processMouseDown()](#._processMouseDown)
+ * [_setApp()](#._setApp)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) / [MouseDispatcher](../lwtk/MouseDispatcher.md#inheritance) / **[Group](../lwtk/Group.md#inheritance)** / [LayoutFrame](../lwtk/LayoutFrame.md#inheritance) / [Control](../lwtk/Control.md#inheritance) / **[Box](../lwtk/Box.md#inheritance)** / [Focusable](../lwtk/Focusable.md#inheritance) / _**`FocusGroup`**_
+
+## Constructor
+ * **`FocusGroup(...)`**
+
+ * Overrides: **[Group()](../lwtk/Group.md#constructor)**
+ * Overrides: [MouseDispatcher()](../lwtk/MouseDispatcher.md#constructor)
+ * Overrides: **[Widget()](../lwtk/Widget.md#constructor)**
+ * Overrides: [Animatable()](../lwtk/Animatable.md#constructor)
+ * Overrides: [Styleable()](../lwtk/Styleable.md#constructor)
+ * Overrides: **[Component()](../lwtk/Component.md#constructor)**
+ * Overrides: [Actionable()](../lwtk/Actionable.md#constructor)
+
+
+
+## Methods
+ * **`FocusGroup:handleHotkey(key, modifier, ...)`**
+
+ * Implements: [Focusable:handleHotkey()](../lwtk/Focusable.md#.handleHotkey)
+
+
+ * **`FocusGroup:invokeActionMethod(actionMethodName)`**
+
+ * Overrides: [Actionable:invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+
+
+ * **`FocusGroup:onActionEnterFocusGroup()`**
+
+
+ * **`FocusGroup:onActionExitFocusGroup()`**
+
+
+ * **`FocusGroup:onKeyDown(key, modifier, ...)`**
+
+ * Implements: [Focusable:onKeyDown()](../lwtk/Focusable.md#.onKeyDown)
+
+
+ * **`FocusGroup:setFocus(flag)`**
+
+ * Overrides: [Focusable:setFocus()](../lwtk/Focusable.md#.setFocus)
+
+
+ * **`FocusGroup:_handleChildRequestsFocus()`**
+
+ * Implements: [Group:_handleChildRequestsFocus()](../lwtk/Group.md#._handleChildRequestsFocus)
+
+
+ * **`FocusGroup:_handleFocusIn()`**
+
+ * Overrides: [Focusable:_handleFocusIn()](../lwtk/Focusable.md#._handleFocusIn)
+ * Implements: [Component:_handleFocusIn()](../lwtk/Component.md#._handleFocusIn)
+
+
+ * **`FocusGroup:_handleFocusOut(reallyLostFocus)`**
+
+ * Overrides: [Focusable:_handleFocusOut()](../lwtk/Focusable.md#._handleFocusOut)
+
+
+ * **`FocusGroup:_processMouseDown(mx, my, button, modState)`**
+
+ * Overrides: [MouseDispatcher:_processMouseDown()](../lwtk/MouseDispatcher.md#._processMouseDown)
+ * Overrides: [Drawable:_processMouseDown()](../lwtk/Drawable.md#._processMouseDown)
+ * Implements: [Node:_processMouseDown()](../lwtk/Node.md#._processMouseDown)
+
+
+ * **`FocusGroup:_setApp(app)`**
+
+ * Overrides: [Widget:_setApp()](../lwtk/Widget.md#._setApp)
+ * Overrides: [Component:_setApp()](../lwtk/Component.md#._setApp)
+
+
+
+## Inherited Methods
+ * [Focusable](../lwtk/Focusable.md):
+ * [onDisabled()](../lwtk/Focusable.md#.onDisabled), [onEffectiveVisibilityChanged()](../lwtk/Focusable.md#.onEffectiveVisibilityChanged), [_handleHasFocusHandler()](../lwtk/Focusable.md#._handleHasFocusHandler)
+ * **[Box](../lwtk/Box.md)**:
+ * [getMeasures()](../lwtk/Box.md#.getMeasures)
+ * [LayoutFrame](../lwtk/LayoutFrame.md):
+ * [addChild()](../lwtk/LayoutFrame.md#.addChild), [onDraw()](../lwtk/LayoutFrame.md#.onDraw), [onLayout()](../lwtk/LayoutFrame.md#.onLayout)
+ * **[Group](../lwtk/Group.md)**:
+ * [childById()](../lwtk/Group.md#.childById), [_clearChildLookup()](../lwtk/Group.md#._clearChildLookup)
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md):
+ * [_processMouseEnter()](../lwtk/MouseDispatcher.md#._processMouseEnter), [_processMouseLeave()](../lwtk/MouseDispatcher.md#._processMouseLeave), [_processMouseMove()](../lwtk/MouseDispatcher.md#._processMouseMove), [_processMouseScroll()](../lwtk/MouseDispatcher.md#._processMouseScroll), [_processMouseUp()](../lwtk/MouseDispatcher.md#._processMouseUp)
+ * [Compound](../lwtk/Compound.md):
+ * [_processChanges()](../lwtk/Compound.md#._processChanges), [_processDraw()](../lwtk/Compound.md#._processDraw)
+ * **[Widget](../lwtk/Widget.md)**:
+ * [notifyInputChanged()](../lwtk/Widget.md#.notifyInputChanged), [setOnInputChanged()](../lwtk/Widget.md#.setOnInputChanged), [setOnRealize()](../lwtk/Widget.md#.setOnRealize), [_setParent()](../lwtk/Widget.md#._setParent)
+ * [Animatable](../lwtk/Animatable.md):
+ * [animateFrame()](../lwtk/Animatable.md#.animateFrame), [getStyle()](../lwtk/Animatable.md#.getStyle), [getStyleParam()](../lwtk/Animatable.md#.getStyleParam), [isVisible()](../lwtk/Animatable.md#.isVisible), [setState()](../lwtk/Animatable.md#.setState), [setStates()](../lwtk/Animatable.md#.setStates), [setStyle()](../lwtk/Animatable.md#.setStyle), [setVisible()](../lwtk/Animatable.md#.setVisible), [updateAnimation()](../lwtk/Animatable.md#.updateAnimation), [_setStyleFromParent()](../lwtk/Animatable.md#._setStyleFromParent)
+ * [Styleable](../lwtk/Styleable.md):
+ * [clearStyleCache()](../lwtk/Styleable.md#.clearStyleCache), [getStateString()](../lwtk/Styleable.md#.getStateString), [_getStyleParam()](../lwtk/Styleable.md#._getStyleParam)
+ * **[Component](../lwtk/Component.md)**:
+ * [byId()](../lwtk/Component.md#.byId), [getCurrentTime()](../lwtk/Component.md#.getCurrentTime), [getFocusHandler()](../lwtk/Component.md#.getFocusHandler), [getFontInfo()](../lwtk/Component.md#.getFontInfo), [getFrame()](../lwtk/Component.md#.getFrame), [getLayoutContext()](../lwtk/Component.md#.getLayoutContext), [getParent()](../lwtk/Component.md#.getParent), [getRoot()](../lwtk/Component.md#.getRoot), [getSize()](../lwtk/Component.md#.getSize), [handleRemainingInitParams()](../lwtk/Component.md#.handleRemainingInitParams), [parentById()](../lwtk/Component.md#.parentById), [setFrame()](../lwtk/Component.md#.setFrame), [setInitParams()](../lwtk/Component.md#.setInitParams), [setTimer()](../lwtk/Component.md#.setTimer), [transformXY()](../lwtk/Component.md#.transformXY), [triggerLayout()](../lwtk/Component.md#.triggerLayout), [triggerRedraw()](../lwtk/Component.md#.triggerRedraw), [updateFrameTransition()](../lwtk/Component.md#.updateFrameTransition), [_setFrame()](../lwtk/Component.md#._setFrame)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/FocusHandler.md b/doc/gen/lwtk/FocusHandler.md
new file mode 100644
index 0000000..92bed50
--- /dev/null
+++ b/doc/gen/lwtk/FocusHandler.md
@@ -0,0 +1,140 @@
+# Class lwtk.FocusHandler
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [childById()](#.childById)
+ * [deregisterHotkeys()](#.deregisterHotkeys)
+ * [findNextInput()](#.findNextInput)
+ * [findPrevInput()](#.findPrevInput)
+ * [handleHotkey()](#.handleHotkey)
+ * [hasActionMethod()](#.hasActionMethod)
+ * [invokeActionMethod()](#.invokeActionMethod)
+ * [isDefault()](#.isDefault)
+ * [onActionCloseWindow()](#.onActionCloseWindow)
+ * [onActionDefaultButton()](#.onActionDefaultButton)
+ * [onActionFocusDown()](#.onActionFocusDown)
+ * [onActionFocusLeft()](#.onActionFocusLeft)
+ * [onActionFocusNext()](#.onActionFocusNext)
+ * [onActionFocusPrev()](#.onActionFocusPrev)
+ * [onActionFocusRight()](#.onActionFocusRight)
+ * [onActionFocusUp()](#.onActionFocusUp)
+ * [onKeyDown()](#.onKeyDown)
+ * [registerHotkeys()](#.registerHotkeys)
+ * [releaseFocus()](#.releaseFocus)
+ * [setDefault()](#.setDefault)
+ * [setFocusDisabled()](#.setFocusDisabled)
+ * [setFocusTo()](#.setFocusTo)
+ * [setFocusToNextInput()](#.setFocusToNextInput)
+ * [setFocusToPrevInput()](#.setFocusToPrevInput)
+ * [_handleFocusIn()](#._handleFocusIn)
+ * [_handleFocusOut()](#._handleFocusOut)
+ * [_setParentFocusHandler()](#._setParentFocusHandler)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / _**`FocusHandler`**_
+
+## Constructor
+ * **`FocusHandler(baseWidget)`**
+
+ * Overrides: [Actionable()](../lwtk/Actionable.md#constructor)
+
+
+
+## Methods
+ * **`FocusHandler:childById(id)`**
+
+
+ * **`FocusHandler:deregisterHotkeys(widget, hotkeys)`**
+
+
+ * **`FocusHandler:findNextInput(child)`**
+
+
+ * **`FocusHandler:findPrevInput(child)`**
+
+
+ * **`FocusHandler:handleHotkey(key, modifier, ...)`**
+
+
+ * **`FocusHandler:hasActionMethod(actionMethodName)`**
+
+ * Overrides: [Actionable:hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod)
+
+
+ * **`FocusHandler:invokeActionMethod(actionMethodName)`**
+
+ * Overrides: [Actionable:invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+
+
+ * **`FocusHandler:isDefault(childOrId)`**
+
+
+ * **`FocusHandler:onActionCloseWindow()`**
+
+
+ * **`FocusHandler:onActionDefaultButton()`**
+
+
+ * **`FocusHandler:onActionFocusDown()`**
+
+
+ * **`FocusHandler:onActionFocusLeft()`**
+
+
+ * **`FocusHandler:onActionFocusNext()`**
+
+
+ * **`FocusHandler:onActionFocusPrev()`**
+
+
+ * **`FocusHandler:onActionFocusRight()`**
+
+
+ * **`FocusHandler:onActionFocusUp()`**
+
+
+ * **`FocusHandler:onKeyDown(key, modifier, keyText, hotKeyName)`**
+
+
+ * **`FocusHandler:registerHotkeys(widget, hotkeys)`**
+
+
+ * **`FocusHandler:releaseFocus(child)`**
+
+
+ * **`FocusHandler:setDefault(childOrId, defaultFlag)`**
+
+
+ * **`FocusHandler:setFocusDisabled(child, disableFlag)`**
+
+
+ * **`FocusHandler:setFocusTo(newFocusChild)`**
+
+
+ * **`FocusHandler:setFocusToNextInput(child)`**
+
+
+ * **`FocusHandler:setFocusToPrevInput(child)`**
+
+
+ * **`FocusHandler:_handleFocusIn()`**
+
+
+ * **`FocusHandler:_handleFocusOut()`**
+
+
+ * **`FocusHandler:_setParentFocusHandler(parentFocusHandler)`**
+
+
+
+## Inherited Methods
+ * [Actionable](../lwtk/Actionable.md):
+ * [handleRemainingInitParams()](../lwtk/Actionable.md#.handleRemainingInitParams), [setInitParams()](../lwtk/Actionable.md#.setInitParams)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/Focusable.md b/doc/gen/lwtk/Focusable.md
new file mode 100644
index 0000000..e385e1a
--- /dev/null
+++ b/doc/gen/lwtk/Focusable.md
@@ -0,0 +1,23 @@
+# Mixin lwtk.Focusable
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) /
+ * [LayoutFrame](../lwtk/LayoutFrame.md#inheritance) / [Control](../lwtk/Control.md#inheritance) /
+ * _`Focusable`_
+ * [HotkeyListener](../lwtk/HotkeyListener.md#inheritance) / **[Button](../lwtk/Button.md#inheritance)** / _`Focusable`_
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md#inheritance) / **[Group](../lwtk/Group.md#inheritance)** / [LayoutFrame](../lwtk/LayoutFrame.md#inheritance) / [Control](../lwtk/Control.md#inheritance) / **[Box](../lwtk/Box.md#inheritance)** / _`Focusable`_
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / [Actionable](../lwtk/Actionable.md#subclasses) / [Node](../lwtk/Node.md#subclasses) / [Drawable](../lwtk/Drawable.md#subclasses) / **[Component](../lwtk/Component.md#subclasses)** / [Styleable](../lwtk/Styleable.md#subclasses) / [Animatable](../lwtk/Animatable.md#subclasses) / **[Widget](../lwtk/Widget.md#subclasses)** / [Compound](../lwtk/Compound.md#subclasses) /
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) /
+ * _`Focusable`_ / **[TextInput](../lwtk/TextInput.md#inheritance)**
+ * [HotkeyListener](../lwtk/HotkeyListener.md#subclasses) / **[Button](../lwtk/Button.md#subclasses)** / _`Focusable`_ / **[PushButton](../lwtk/PushButton.md#inheritance)**
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Group](../lwtk/Group.md#subclasses)** / [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) / **[Box](../lwtk/Box.md#subclasses)** / _`Focusable`_ / **[FocusGroup](../lwtk/FocusGroup.md#inheritance)**
+
diff --git a/doc/gen/lwtk/FontInfo.md b/doc/gen/lwtk/FontInfo.md
new file mode 100644
index 0000000..62be7dc
--- /dev/null
+++ b/doc/gen/lwtk/FontInfo.md
@@ -0,0 +1,32 @@
+# Class lwtk.FontInfo
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [getTextWidth()](#.getTextWidth)
+ * [selectInto()](#.selectInto)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / _**`FontInfo`**_
+
+## Constructor
+ * **`FontInfo(layoutContext, family, slant, weight, size)`**
+
+
+
+## Methods
+ * **`FontInfo:getTextWidth(text)`**
+
+
+ * **`FontInfo:selectInto(otherCtx)`**
+
+
+
+## Inherited Methods
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/FontInfos.md b/doc/gen/lwtk/FontInfos.md
new file mode 100644
index 0000000..ce92362
--- /dev/null
+++ b/doc/gen/lwtk/FontInfos.md
@@ -0,0 +1,28 @@
+# Class lwtk.FontInfos
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [getFontInfo()](#.getFontInfo)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / _**`FontInfos`**_
+
+## Constructor
+ * **`FontInfos(layoutContext)`**
+
+
+
+## Methods
+ * **`FontInfos:getFontInfo(family, slant, weight, size)`**
+
+
+
+## Inherited Methods
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/Group.md b/doc/gen/lwtk/Group.md
new file mode 100644
index 0000000..e48630b
--- /dev/null
+++ b/doc/gen/lwtk/Group.md
@@ -0,0 +1,74 @@
+# Class lwtk.Group
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [addChild()](#.addChild)
+ * [childById()](#.childById)
+ * [_clearChildLookup()](#._clearChildLookup)
+ * [Inherited Methods](#inherited-methods)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) / [MouseDispatcher](../lwtk/MouseDispatcher.md#inheritance) / _**`Group`**_
+
+## Constructor
+ * **`Group(initParams)`**
+
+ * Overrides: [MouseDispatcher()](../lwtk/MouseDispatcher.md#constructor)
+ * Overrides: **[Widget()](../lwtk/Widget.md#constructor)**
+ * Overrides: [Animatable()](../lwtk/Animatable.md#constructor)
+ * Overrides: [Styleable()](../lwtk/Styleable.md#constructor)
+ * Overrides: **[Component()](../lwtk/Component.md#constructor)**
+ * Overrides: [Actionable()](../lwtk/Actionable.md#constructor)
+
+
+
+## Methods
+ * **`Group:addChild(child)`**
+
+ * Overrides: [Compound:addChild()](../lwtk/Compound.md#.addChild)
+ * Implements: [Drawable:addChild()](../lwtk/Drawable.md#.addChild)
+
+
+ * **`Group:childById(id)`**
+
+
+ * **`Group:_clearChildLookup()`**
+
+
+
+## Inherited Methods
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md):
+ * [_processMouseDown()](../lwtk/MouseDispatcher.md#._processMouseDown), [_processMouseEnter()](../lwtk/MouseDispatcher.md#._processMouseEnter), [_processMouseLeave()](../lwtk/MouseDispatcher.md#._processMouseLeave), [_processMouseMove()](../lwtk/MouseDispatcher.md#._processMouseMove), [_processMouseScroll()](../lwtk/MouseDispatcher.md#._processMouseScroll), [_processMouseUp()](../lwtk/MouseDispatcher.md#._processMouseUp)
+ * [Compound](../lwtk/Compound.md):
+ * [_processChanges()](../lwtk/Compound.md#._processChanges), [_processDraw()](../lwtk/Compound.md#._processDraw)
+ * **[Widget](../lwtk/Widget.md)**:
+ * [notifyInputChanged()](../lwtk/Widget.md#.notifyInputChanged), [setOnInputChanged()](../lwtk/Widget.md#.setOnInputChanged), [setOnRealize()](../lwtk/Widget.md#.setOnRealize), [_setApp()](../lwtk/Widget.md#._setApp), [_setParent()](../lwtk/Widget.md#._setParent)
+ * [Animatable](../lwtk/Animatable.md):
+ * [animateFrame()](../lwtk/Animatable.md#.animateFrame), [getStyle()](../lwtk/Animatable.md#.getStyle), [getStyleParam()](../lwtk/Animatable.md#.getStyleParam), [isVisible()](../lwtk/Animatable.md#.isVisible), [setState()](../lwtk/Animatable.md#.setState), [setStates()](../lwtk/Animatable.md#.setStates), [setStyle()](../lwtk/Animatable.md#.setStyle), [setVisible()](../lwtk/Animatable.md#.setVisible), [updateAnimation()](../lwtk/Animatable.md#.updateAnimation), [_setStyleFromParent()](../lwtk/Animatable.md#._setStyleFromParent)
+ * [Styleable](../lwtk/Styleable.md):
+ * [clearStyleCache()](../lwtk/Styleable.md#.clearStyleCache), [getStateString()](../lwtk/Styleable.md#.getStateString), [_getStyleParam()](../lwtk/Styleable.md#._getStyleParam)
+ * **[Component](../lwtk/Component.md)**:
+ * [byId()](../lwtk/Component.md#.byId), [getCurrentTime()](../lwtk/Component.md#.getCurrentTime), [getFocusHandler()](../lwtk/Component.md#.getFocusHandler), [getFontInfo()](../lwtk/Component.md#.getFontInfo), [getFrame()](../lwtk/Component.md#.getFrame), [getLayoutContext()](../lwtk/Component.md#.getLayoutContext), [getParent()](../lwtk/Component.md#.getParent), [getRoot()](../lwtk/Component.md#.getRoot), [getSize()](../lwtk/Component.md#.getSize), [handleRemainingInitParams()](../lwtk/Component.md#.handleRemainingInitParams), [parentById()](../lwtk/Component.md#.parentById), [setFrame()](../lwtk/Component.md#.setFrame), [setInitParams()](../lwtk/Component.md#.setInitParams), [setTimer()](../lwtk/Component.md#.setTimer), [transformXY()](../lwtk/Component.md#.transformXY), [triggerLayout()](../lwtk/Component.md#.triggerLayout), [triggerRedraw()](../lwtk/Component.md#.triggerRedraw), [updateFrameTransition()](../lwtk/Component.md#.updateFrameTransition), [_setFrame()](../lwtk/Component.md#._setFrame)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / [Actionable](../lwtk/Actionable.md#subclasses) / [Node](../lwtk/Node.md#subclasses) / [Drawable](../lwtk/Drawable.md#subclasses) / **[Component](../lwtk/Component.md#subclasses)** / [Styleable](../lwtk/Styleable.md#subclasses) / [Animatable](../lwtk/Animatable.md#subclasses) / **[Widget](../lwtk/Widget.md#subclasses)** / [Compound](../lwtk/Compound.md#subclasses) / [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / _**`Group`**_ /
+ * [Colored](../lwtk/Colored.md#subclasses) / **[ViewSwitcher](../lwtk/ViewSwitcher.md#inheritance)**
+ * **[Column](../lwtk/Column.md#inheritance)**
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) / **[Box](../lwtk/Box.md#subclasses)** / [Focusable](../lwtk/Focusable.md#subclasses) / **[FocusGroup](../lwtk/FocusGroup.md#inheritance)**
+ * **[Matrix](../lwtk/Matrix.md#inheritance)**
+ * **[Row](../lwtk/Row.md#inheritance)**
+ * **[Space](../lwtk/Space.md#inheritance)**
+ * **[Square](../lwtk/Square.md#inheritance)**
+
diff --git a/doc/gen/lwtk/HotkeyListener.md b/doc/gen/lwtk/HotkeyListener.md
new file mode 100644
index 0000000..51cc054
--- /dev/null
+++ b/doc/gen/lwtk/HotkeyListener.md
@@ -0,0 +1,17 @@
+# Mixin lwtk.HotkeyListener
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) / [LayoutFrame](../lwtk/LayoutFrame.md#inheritance) / [Control](../lwtk/Control.md#inheritance) / _`HotkeyListener`_
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / [Actionable](../lwtk/Actionable.md#subclasses) / [Node](../lwtk/Node.md#subclasses) / [Drawable](../lwtk/Drawable.md#subclasses) / **[Component](../lwtk/Component.md#subclasses)** / [Styleable](../lwtk/Styleable.md#subclasses) / [Animatable](../lwtk/Animatable.md#subclasses) / **[Widget](../lwtk/Widget.md#subclasses)** / [Compound](../lwtk/Compound.md#subclasses) / [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) / _`HotkeyListener`_ / **[Button](../lwtk/Button.md#subclasses)** /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[PushButton](../lwtk/PushButton.md#inheritance)**
+ * **[TextLabel](../lwtk/TextLabel.md#subclasses)** / **[TitleText](../lwtk/TitleText.md#inheritance)**
+
diff --git a/doc/gen/lwtk/InnerCompound.md b/doc/gen/lwtk/InnerCompound.md
new file mode 100644
index 0000000..e1ae0e1
--- /dev/null
+++ b/doc/gen/lwtk/InnerCompound.md
@@ -0,0 +1,30 @@
+# Class lwtk.InnerCompound
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) / _**`InnerCompound`**_
+
+## Constructor
+ * **`InnerCompound(initParams)`**
+
+ * Inherited from: **[Component()](../lwtk/Component.md#constructor)**
+
+
+## Inherited Methods
+ * [Compound](../lwtk/Compound.md):
+ * [addChild()](../lwtk/Compound.md#.addChild), [_processChanges()](../lwtk/Compound.md#._processChanges), [_processDraw()](../lwtk/Compound.md#._processDraw)
+ * **[Component](../lwtk/Component.md)**:
+ * [animateFrame()](../lwtk/Component.md#.animateFrame), [byId()](../lwtk/Component.md#.byId), [getCurrentTime()](../lwtk/Component.md#.getCurrentTime), [getFocusHandler()](../lwtk/Component.md#.getFocusHandler), [getFontInfo()](../lwtk/Component.md#.getFontInfo), [getFrame()](../lwtk/Component.md#.getFrame), [getLayoutContext()](../lwtk/Component.md#.getLayoutContext), [getParent()](../lwtk/Component.md#.getParent), [getRoot()](../lwtk/Component.md#.getRoot), [getSize()](../lwtk/Component.md#.getSize), [handleRemainingInitParams()](../lwtk/Component.md#.handleRemainingInitParams), [parentById()](../lwtk/Component.md#.parentById), [setFrame()](../lwtk/Component.md#.setFrame), [setInitParams()](../lwtk/Component.md#.setInitParams), [setTimer()](../lwtk/Component.md#.setTimer), [transformXY()](../lwtk/Component.md#.transformXY), [triggerLayout()](../lwtk/Component.md#.triggerLayout), [triggerRedraw()](../lwtk/Component.md#.triggerRedraw), [updateAnimation()](../lwtk/Component.md#.updateAnimation), [updateFrameTransition()](../lwtk/Component.md#.updateFrameTransition), [_setApp()](../lwtk/Component.md#._setApp), [_setFrame()](../lwtk/Component.md#._setFrame), [_setParent()](../lwtk/Component.md#._setParent)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam), [getStyleParam()](../lwtk/Drawable.md#.getStyleParam), [_processMouseDown()](../lwtk/Drawable.md#._processMouseDown), [_processMouseEnter()](../lwtk/Drawable.md#._processMouseEnter), [_processMouseLeave()](../lwtk/Drawable.md#._processMouseLeave), [_processMouseMove()](../lwtk/Drawable.md#._processMouseMove), [_processMouseScroll()](../lwtk/Drawable.md#._processMouseScroll), [_processMouseUp()](../lwtk/Drawable.md#._processMouseUp)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/KeyBinding.md b/doc/gen/lwtk/KeyBinding.md
new file mode 100644
index 0000000..80ccb20
--- /dev/null
+++ b/doc/gen/lwtk/KeyBinding.md
@@ -0,0 +1,12 @@
+# Meta lwtk.KeyBinding
+
+
+## Contents
+
+ * [Constructor](#constructor)
+
+
+## Constructor
+ * **`KeyBinding(rules)`**
+
+
diff --git a/doc/gen/lwtk/KeyHandler.md b/doc/gen/lwtk/KeyHandler.md
new file mode 100644
index 0000000..4662f4a
--- /dev/null
+++ b/doc/gen/lwtk/KeyHandler.md
@@ -0,0 +1,15 @@
+# Mixin lwtk.KeyHandler
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / [Styleable](../lwtk/Styleable.md#inheritance) / _`KeyHandler`_
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / [Actionable](../lwtk/Actionable.md#subclasses) / [Node](../lwtk/Node.md#subclasses) / [Drawable](../lwtk/Drawable.md#subclasses) / [Styleable](../lwtk/Styleable.md#subclasses) / _`KeyHandler`_ / [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Window](../lwtk/Window.md#inheritance)**
+
diff --git a/doc/gen/lwtk/LayoutFrame.md b/doc/gen/lwtk/LayoutFrame.md
new file mode 100644
index 0000000..df734fc
--- /dev/null
+++ b/doc/gen/lwtk/LayoutFrame.md
@@ -0,0 +1,23 @@
+# Mixin lwtk.LayoutFrame
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) /
+ * _`LayoutFrame`_
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md#inheritance) / **[Group](../lwtk/Group.md#inheritance)** / _`LayoutFrame`_
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / [Actionable](../lwtk/Actionable.md#subclasses) / [Node](../lwtk/Node.md#subclasses) / [Drawable](../lwtk/Drawable.md#subclasses) / **[Component](../lwtk/Component.md#subclasses)** / [Styleable](../lwtk/Styleable.md#subclasses) / [Animatable](../lwtk/Animatable.md#subclasses) / **[Widget](../lwtk/Widget.md#subclasses)** / [Compound](../lwtk/Compound.md#subclasses) /
+ * _`LayoutFrame`_ / [Control](../lwtk/Control.md#subclasses) /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[TextInput](../lwtk/TextInput.md#inheritance)**
+ * [HotkeyListener](../lwtk/HotkeyListener.md#subclasses) / **[Button](../lwtk/Button.md#subclasses)** /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[PushButton](../lwtk/PushButton.md#inheritance)**
+ * **[TextLabel](../lwtk/TextLabel.md#subclasses)** / **[TitleText](../lwtk/TitleText.md#inheritance)**
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Group](../lwtk/Group.md#subclasses)** / _`LayoutFrame`_ / [Control](../lwtk/Control.md#subclasses) / **[Box](../lwtk/Box.md#subclasses)** / [Focusable](../lwtk/Focusable.md#subclasses) / **[FocusGroup](../lwtk/FocusGroup.md#inheritance)**
+
diff --git a/doc/gen/lwtk/Matrix.md b/doc/gen/lwtk/Matrix.md
new file mode 100644
index 0000000..d436f0f
--- /dev/null
+++ b/doc/gen/lwtk/Matrix.md
@@ -0,0 +1,62 @@
+# Class lwtk.Matrix
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [getMeasures()](#.getMeasures)
+ * [onLayout()](#.onLayout)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) / [MouseDispatcher](../lwtk/MouseDispatcher.md#inheritance) / **[Group](../lwtk/Group.md#inheritance)** / _**`Matrix`**_
+
+## Constructor
+ * **`Matrix(initParams)`**
+
+ * Overrides: **[Group()](../lwtk/Group.md#constructor)**
+ * Overrides: [MouseDispatcher()](../lwtk/MouseDispatcher.md#constructor)
+ * Overrides: **[Widget()](../lwtk/Widget.md#constructor)**
+ * Overrides: [Animatable()](../lwtk/Animatable.md#constructor)
+ * Overrides: [Styleable()](../lwtk/Styleable.md#constructor)
+ * Overrides: **[Component()](../lwtk/Component.md#constructor)**
+ * Overrides: [Actionable()](../lwtk/Actionable.md#constructor)
+
+
+
+## Methods
+ * **`Matrix:getMeasures()`**
+
+ * Implements: [Component:getMeasures()](../lwtk/Component.md#.getMeasures)
+
+
+ * **`Matrix:onLayout(width, height, isLayoutTransition)`**
+
+ * Implements: [Component:onLayout()](../lwtk/Component.md#.onLayout)
+
+
+
+## Inherited Methods
+ * **[Group](../lwtk/Group.md)**:
+ * [addChild()](../lwtk/Group.md#.addChild), [childById()](../lwtk/Group.md#.childById), [_clearChildLookup()](../lwtk/Group.md#._clearChildLookup)
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md):
+ * [_processMouseDown()](../lwtk/MouseDispatcher.md#._processMouseDown), [_processMouseEnter()](../lwtk/MouseDispatcher.md#._processMouseEnter), [_processMouseLeave()](../lwtk/MouseDispatcher.md#._processMouseLeave), [_processMouseMove()](../lwtk/MouseDispatcher.md#._processMouseMove), [_processMouseScroll()](../lwtk/MouseDispatcher.md#._processMouseScroll), [_processMouseUp()](../lwtk/MouseDispatcher.md#._processMouseUp)
+ * [Compound](../lwtk/Compound.md):
+ * [_processChanges()](../lwtk/Compound.md#._processChanges), [_processDraw()](../lwtk/Compound.md#._processDraw)
+ * **[Widget](../lwtk/Widget.md)**:
+ * [notifyInputChanged()](../lwtk/Widget.md#.notifyInputChanged), [setOnInputChanged()](../lwtk/Widget.md#.setOnInputChanged), [setOnRealize()](../lwtk/Widget.md#.setOnRealize), [_setApp()](../lwtk/Widget.md#._setApp), [_setParent()](../lwtk/Widget.md#._setParent)
+ * [Animatable](../lwtk/Animatable.md):
+ * [animateFrame()](../lwtk/Animatable.md#.animateFrame), [getStyle()](../lwtk/Animatable.md#.getStyle), [getStyleParam()](../lwtk/Animatable.md#.getStyleParam), [isVisible()](../lwtk/Animatable.md#.isVisible), [setState()](../lwtk/Animatable.md#.setState), [setStates()](../lwtk/Animatable.md#.setStates), [setStyle()](../lwtk/Animatable.md#.setStyle), [setVisible()](../lwtk/Animatable.md#.setVisible), [updateAnimation()](../lwtk/Animatable.md#.updateAnimation), [_setStyleFromParent()](../lwtk/Animatable.md#._setStyleFromParent)
+ * [Styleable](../lwtk/Styleable.md):
+ * [clearStyleCache()](../lwtk/Styleable.md#.clearStyleCache), [getStateString()](../lwtk/Styleable.md#.getStateString), [_getStyleParam()](../lwtk/Styleable.md#._getStyleParam)
+ * **[Component](../lwtk/Component.md)**:
+ * [byId()](../lwtk/Component.md#.byId), [getCurrentTime()](../lwtk/Component.md#.getCurrentTime), [getFocusHandler()](../lwtk/Component.md#.getFocusHandler), [getFontInfo()](../lwtk/Component.md#.getFontInfo), [getFrame()](../lwtk/Component.md#.getFrame), [getLayoutContext()](../lwtk/Component.md#.getLayoutContext), [getParent()](../lwtk/Component.md#.getParent), [getRoot()](../lwtk/Component.md#.getRoot), [getSize()](../lwtk/Component.md#.getSize), [handleRemainingInitParams()](../lwtk/Component.md#.handleRemainingInitParams), [parentById()](../lwtk/Component.md#.parentById), [setFrame()](../lwtk/Component.md#.setFrame), [setInitParams()](../lwtk/Component.md#.setInitParams), [setTimer()](../lwtk/Component.md#.setTimer), [transformXY()](../lwtk/Component.md#.transformXY), [triggerLayout()](../lwtk/Component.md#.triggerLayout), [triggerRedraw()](../lwtk/Component.md#.triggerRedraw), [updateFrameTransition()](../lwtk/Component.md#.updateFrameTransition), [_setFrame()](../lwtk/Component.md#._setFrame)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/Meta.md b/doc/gen/lwtk/Meta.md
new file mode 100644
index 0000000..6e88ec6
--- /dev/null
+++ b/doc/gen/lwtk/Meta.md
@@ -0,0 +1,7 @@
+# Table lwtk.Meta
+
+Metatable for objects created by [lwtk.newMeta](../lwtk/newMeta.md)().
+
+See [lwtk,Meta Usage](../../Meta.md) for detailed documentation
+and usage examples.
+
diff --git a/doc/gen/lwtk/Mixin.md b/doc/gen/lwtk/Mixin.md
new file mode 100644
index 0000000..799b4a6
--- /dev/null
+++ b/doc/gen/lwtk/Mixin.md
@@ -0,0 +1,7 @@
+# Table lwtk.Mixin
+
+Metatable for objects created by [lwtk.newMixin](../lwtk/newMixin.md)().
+
+See [lwtk.Mixin Usage](../../Mixin.md) for detailed documentation
+and usage examples.
+
diff --git a/doc/gen/lwtk/MouseDispatcher.md b/doc/gen/lwtk/MouseDispatcher.md
new file mode 100644
index 0000000..a04262d
--- /dev/null
+++ b/doc/gen/lwtk/MouseDispatcher.md
@@ -0,0 +1,30 @@
+# Mixin lwtk.MouseDispatcher
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** /
+ * [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) /
+ * **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) / _`MouseDispatcher`_
+ * [Styleable](../lwtk/Styleable.md#inheritance) / [KeyHandler](../lwtk/KeyHandler.md#inheritance) / _`MouseDispatcher`_
+ * **[Application](../lwtk/Application.md#inheritance)** / [Node](../lwtk/Node.md#inheritance) / _`MouseDispatcher`_
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** /
+ * [Actionable](../lwtk/Actionable.md#subclasses) / [Node](../lwtk/Node.md#subclasses) / [Drawable](../lwtk/Drawable.md#subclasses) /
+ * **[Component](../lwtk/Component.md#subclasses)** / [Styleable](../lwtk/Styleable.md#subclasses) / [Animatable](../lwtk/Animatable.md#subclasses) / **[Widget](../lwtk/Widget.md#subclasses)** / [Compound](../lwtk/Compound.md#subclasses) / _`MouseDispatcher`_ / **[Group](../lwtk/Group.md#subclasses)** /
+ * [Colored](../lwtk/Colored.md#subclasses) / **[ViewSwitcher](../lwtk/ViewSwitcher.md#inheritance)**
+ * **[Column](../lwtk/Column.md#inheritance)**
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) / **[Box](../lwtk/Box.md#subclasses)** / [Focusable](../lwtk/Focusable.md#subclasses) / **[FocusGroup](../lwtk/FocusGroup.md#inheritance)**
+ * **[Matrix](../lwtk/Matrix.md#inheritance)**
+ * **[Row](../lwtk/Row.md#inheritance)**
+ * **[Space](../lwtk/Space.md#inheritance)**
+ * **[Square](../lwtk/Square.md#inheritance)**
+ * [Styleable](../lwtk/Styleable.md#subclasses) / [KeyHandler](../lwtk/KeyHandler.md#subclasses) / _`MouseDispatcher`_ / **[Window](../lwtk/Window.md#inheritance)**
+ * **[Application](../lwtk/Application.md#subclasses)** / [Node](../lwtk/Node.md#subclasses) / _`MouseDispatcher`_ / **[love.Application](../lwtk/love/Application.md#inheritance)**
+
diff --git a/doc/gen/lwtk/Node.md b/doc/gen/lwtk/Node.md
new file mode 100644
index 0000000..293ea32
--- /dev/null
+++ b/doc/gen/lwtk/Node.md
@@ -0,0 +1,38 @@
+# Mixin lwtk.Node
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** /
+ * [Actionable](../lwtk/Actionable.md#inheritance) / _`Node`_
+ * **[Application](../lwtk/Application.md#inheritance)** / _`Node`_
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** /
+ * [Actionable](../lwtk/Actionable.md#subclasses) / _`Node`_ / [Drawable](../lwtk/Drawable.md#subclasses) /
+ * **[Component](../lwtk/Component.md#subclasses)** /
+ * [Compound](../lwtk/Compound.md#subclasses) / **[InnerCompound](../lwtk/InnerCompound.md#inheritance)**
+ * [Styleable](../lwtk/Styleable.md#subclasses) / [Animatable](../lwtk/Animatable.md#subclasses) / **[Widget](../lwtk/Widget.md#subclasses)** / [Compound](../lwtk/Compound.md#subclasses) /
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[TextInput](../lwtk/TextInput.md#inheritance)**
+ * [HotkeyListener](../lwtk/HotkeyListener.md#subclasses) / **[Button](../lwtk/Button.md#subclasses)** /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[PushButton](../lwtk/PushButton.md#inheritance)**
+ * **[TextLabel](../lwtk/TextLabel.md#subclasses)** / **[TitleText](../lwtk/TitleText.md#inheritance)**
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Group](../lwtk/Group.md#subclasses)** /
+ * [Colored](../lwtk/Colored.md#subclasses) / **[ViewSwitcher](../lwtk/ViewSwitcher.md#inheritance)**
+ * **[Column](../lwtk/Column.md#inheritance)**
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) / **[Box](../lwtk/Box.md#subclasses)** / [Focusable](../lwtk/Focusable.md#subclasses) / **[FocusGroup](../lwtk/FocusGroup.md#inheritance)**
+ * **[Matrix](../lwtk/Matrix.md#inheritance)**
+ * **[Row](../lwtk/Row.md#inheritance)**
+ * **[Space](../lwtk/Space.md#inheritance)**
+ * **[Square](../lwtk/Square.md#inheritance)**
+ * **[TextCursor](../lwtk/TextCursor.md#inheritance)**
+ * **[TextFragment](../lwtk/TextFragment.md#inheritance)**
+ * [Styleable](../lwtk/Styleable.md#subclasses) / [KeyHandler](../lwtk/KeyHandler.md#subclasses) / [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Window](../lwtk/Window.md#inheritance)**
+ * **[Application](../lwtk/Application.md#subclasses)** / _`Node`_ / [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[love.Application](../lwtk/love/Application.md#inheritance)**
+
diff --git a/doc/gen/lwtk/Object.md b/doc/gen/lwtk/Object.md
new file mode 100644
index 0000000..e153c1e
--- /dev/null
+++ b/doc/gen/lwtk/Object.md
@@ -0,0 +1,91 @@
+# Class lwtk.Object
+
+Superclass for all classes created by [lwtk.newClass](../lwtk/newClass.md)().
+
+## Contents
+
+ * [Methods](#methods)
+ * [getClass()](#.getClass)
+ * [getClassPath()](#.getClassPath)
+ * [getMember()](#.getMember)
+ * [getReverseClassPath()](#.getReverseClassPath)
+ * [getSuperClass()](#.getSuperClass) - Returns the superclass of this object.
+ * [isInstanceOf()](#.isInstanceOf) - Determines if object is instance of the given class.
+ * [setAttributes()](#.setAttributes)
+ * [Subclasses](#subclasses)
+
+
+## Methods
+ * **`Object:getClass()`**
+
+
+ * **`Object:getClassPath()`**
+
+
+ * **`Object:getMember(name)`**
+
+
+ * **`Object:getReverseClassPath()`**
+
+
+ * **`Object:getSuperClass()`**
+
+ Returns the superclass of this object.
+
+ This method can also be called on a class. In this
+ case the superclass of the given class is returned.
+
+ * **`Object:isInstanceOf(C)`**
+
+ Determines if object is instance of the given class.
+
+ Returns *true*, if *self* is an object that was created by invoking class *C*
+ or by invoking a subclass of *C*.
+
+ Returns also *true* if *C* is a metatable of *self* or somewhere in the
+ metatable chain of *self*.
+ * Implementation: [lwtk.isInstanceOf()](../lwtk/isInstanceOf.md)
+
+ * **`Object:setAttributes(attr)`**
+
+
+
+## Subclasses
+ * / _**`Object`**_ /
+ * [Actionable](../lwtk/Actionable.md#subclasses) /
+ * **[FocusHandler](../lwtk/FocusHandler.md#inheritance)**
+ * [Node](../lwtk/Node.md#subclasses) / [Drawable](../lwtk/Drawable.md#subclasses) /
+ * **[Component](../lwtk/Component.md#subclasses)** /
+ * [Compound](../lwtk/Compound.md#subclasses) / **[InnerCompound](../lwtk/InnerCompound.md#inheritance)**
+ * [Styleable](../lwtk/Styleable.md#subclasses) / [Animatable](../lwtk/Animatable.md#subclasses) / **[Widget](../lwtk/Widget.md#subclasses)** / [Compound](../lwtk/Compound.md#subclasses) /
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[TextInput](../lwtk/TextInput.md#inheritance)**
+ * [HotkeyListener](../lwtk/HotkeyListener.md#subclasses) / **[Button](../lwtk/Button.md#subclasses)** /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[PushButton](../lwtk/PushButton.md#inheritance)**
+ * **[TextLabel](../lwtk/TextLabel.md#subclasses)** / **[TitleText](../lwtk/TitleText.md#inheritance)**
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Group](../lwtk/Group.md#subclasses)** /
+ * [Colored](../lwtk/Colored.md#subclasses) / **[ViewSwitcher](../lwtk/ViewSwitcher.md#inheritance)**
+ * **[Column](../lwtk/Column.md#inheritance)**
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) / **[Box](../lwtk/Box.md#subclasses)** / [Focusable](../lwtk/Focusable.md#subclasses) / **[FocusGroup](../lwtk/FocusGroup.md#inheritance)**
+ * **[Matrix](../lwtk/Matrix.md#inheritance)**
+ * **[Row](../lwtk/Row.md#inheritance)**
+ * **[Space](../lwtk/Space.md#inheritance)**
+ * **[Square](../lwtk/Square.md#inheritance)**
+ * **[TextCursor](../lwtk/TextCursor.md#inheritance)**
+ * **[TextFragment](../lwtk/TextFragment.md#inheritance)**
+ * [Styleable](../lwtk/Styleable.md#subclasses) / [KeyHandler](../lwtk/KeyHandler.md#subclasses) / [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Window](../lwtk/Window.md#inheritance)**
+ * **[Animations](../lwtk/Animations.md#inheritance)**
+ * **[Application](../lwtk/Application.md#subclasses)** / [Node](../lwtk/Node.md#subclasses) / [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[love.Application](../lwtk/love/Application.md#inheritance)**
+ * **[Area](../lwtk/Area.md#inheritance)**
+ * **[Color](../lwtk/Color.md#inheritance)**
+ * **[FontInfo](../lwtk/FontInfo.md#inheritance)**
+ * **[FontInfos](../lwtk/FontInfos.md#inheritance)**
+ * **[Rect](../lwtk/Rect.md#inheritance)**
+ * **[Style](../lwtk/Style.md#subclasses)** / **[DefaultStyle](../lwtk/DefaultStyle.md#inheritance)**
+ * **[Timer](../lwtk/Timer.md#inheritance)**
+ * **[love.Driver](../lwtk/love/Driver.md#inheritance)**
+ * **[love.LayoutContext](../lwtk/love/LayoutContext.md#subclasses)** / **[love.DrawContext](../lwtk/love/DrawContext.md#inheritance)**
+ * **[love.View](../lwtk/love/View.md#inheritance)**
+ * **[lpugl.CairoLayoutContext](../lwtk/lpugl/CairoLayoutContext.md#subclasses)** / **[lpugl.CairoDrawContext](../lwtk/lpugl/CairoDrawContext.md#inheritance)**
+ * **[lpugl.Driver](../lwtk/lpugl/Driver.md#inheritance)**
+
diff --git a/doc/gen/lwtk/PushButton.md b/doc/gen/lwtk/PushButton.md
new file mode 100644
index 0000000..9ce80eb
--- /dev/null
+++ b/doc/gen/lwtk/PushButton.md
@@ -0,0 +1,132 @@
+# Class lwtk.PushButton
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [getMeasures()](#.getMeasures)
+ * [onActionFocusedButtonClick()](#.onActionFocusedButtonClick)
+ * [onDefaultChanged()](#.onDefaultChanged)
+ * [onHotkeyDisabled()](#.onHotkeyDisabled)
+ * [onHotkeyDown()](#.onHotkeyDown)
+ * [onHotkeyEnabled()](#.onHotkeyEnabled)
+ * [onLayout()](#.onLayout)
+ * [onMouseDown()](#.onMouseDown)
+ * [onMouseEnter()](#.onMouseEnter)
+ * [onMouseLeave()](#.onMouseLeave)
+ * [onMouseUp()](#.onMouseUp)
+ * [setDefault()](#.setDefault)
+ * [setDisabled()](#.setDisabled)
+ * [setOnClicked()](#.setOnClicked)
+ * [setText()](#.setText)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) / [LayoutFrame](../lwtk/LayoutFrame.md#inheritance) / [Control](../lwtk/Control.md#inheritance) / [HotkeyListener](../lwtk/HotkeyListener.md#inheritance) / **[Button](../lwtk/Button.md#inheritance)** / [Focusable](../lwtk/Focusable.md#inheritance) / _**`PushButton`**_
+
+## Constructor
+ * **`PushButton(initParams)`**
+
+ * Overrides: **[Widget()](../lwtk/Widget.md#constructor)**
+ * Overrides: [Animatable()](../lwtk/Animatable.md#constructor)
+ * Overrides: [Styleable()](../lwtk/Styleable.md#constructor)
+ * Overrides: **[Component()](../lwtk/Component.md#constructor)**
+ * Overrides: [Actionable()](../lwtk/Actionable.md#constructor)
+
+
+
+## Methods
+ * **`PushButton:getMeasures()`**
+
+ * Implementation: [TextLabel:getMeasures()](../lwtk/TextLabel.md#getMeasures)
+ * Implements: [Component:getMeasures()](../lwtk/Component.md#.getMeasures)
+
+
+ * **`PushButton:onActionFocusedButtonClick()`**
+
+
+ * **`PushButton:onDefaultChanged(isCurrentDefault, isPrincipalDefault)`**
+
+
+ * **`PushButton:onHotkeyDisabled(hotkey)`**
+
+ * Overrides: [HotkeyListener:onHotkeyDisabled()](../lwtk/HotkeyListener.md#.onHotkeyDisabled)
+
+
+ * **`PushButton:onHotkeyDown()`**
+
+ * Implements: [Focusable:onHotkeyDown()](../lwtk/Focusable.md#.onHotkeyDown)
+
+
+ * **`PushButton:onHotkeyEnabled(hotkey)`**
+
+ * Overrides: [HotkeyListener:onHotkeyEnabled()](../lwtk/HotkeyListener.md#.onHotkeyEnabled)
+ * Implements: [Component:onHotkeyEnabled()](../lwtk/Component.md#.onHotkeyEnabled)
+
+
+ * **`PushButton:onLayout(width, height)`**
+
+ * Overrides: [LayoutFrame:onLayout()](../lwtk/LayoutFrame.md#.onLayout)
+ * Implements: [Component:onLayout()](../lwtk/Component.md#.onLayout)
+
+
+ * **`PushButton:onMouseDown(x, y, button, modState)`**
+
+ * Implements: [Node:onMouseDown()](../lwtk/Node.md#.onMouseDown)
+
+
+ * **`PushButton:onMouseEnter(x, y)`**
+
+ * Implements: [Node:onMouseEnter()](../lwtk/Node.md#.onMouseEnter)
+
+
+ * **`PushButton:onMouseLeave(x, y)`**
+
+ * Implements: [Node:onMouseLeave()](../lwtk/Node.md#.onMouseLeave)
+
+
+ * **`PushButton:onMouseUp(x, y, button, modState)`**
+
+ * Implements: [Node:onMouseUp()](../lwtk/Node.md#.onMouseUp)
+
+
+ * **`PushButton:setDefault(defaultFlag)`**
+
+ * Implementation: [Focusable:extra.setDefault()](../lwtk/Focusable.md#extra.setDefault)
+
+ * **`PushButton:setDisabled(flag)`**
+
+
+ * **`PushButton:setOnClicked(onClicked)`**
+
+
+ * **`PushButton:setText(text)`**
+
+
+
+## Inherited Methods
+ * [Focusable](../lwtk/Focusable.md):
+ * [onDisabled()](../lwtk/Focusable.md#.onDisabled), [onEffectiveVisibilityChanged()](../lwtk/Focusable.md#.onEffectiveVisibilityChanged), [setFocus()](../lwtk/Focusable.md#.setFocus), [_handleFocusIn()](../lwtk/Focusable.md#._handleFocusIn), [_handleFocusOut()](../lwtk/Focusable.md#._handleFocusOut), [_handleHasFocusHandler()](../lwtk/Focusable.md#._handleHasFocusHandler)
+ * [HotkeyListener](../lwtk/HotkeyListener.md):
+ * [isHotkeyEnabled()](../lwtk/HotkeyListener.md#.isHotkeyEnabled), [setHotkey()](../lwtk/HotkeyListener.md#.setHotkey)
+ * [LayoutFrame](../lwtk/LayoutFrame.md):
+ * [addChild()](../lwtk/LayoutFrame.md#.addChild), [onDraw()](../lwtk/LayoutFrame.md#.onDraw)
+ * [Compound](../lwtk/Compound.md):
+ * [_processChanges()](../lwtk/Compound.md#._processChanges), [_processDraw()](../lwtk/Compound.md#._processDraw)
+ * **[Widget](../lwtk/Widget.md)**:
+ * [notifyInputChanged()](../lwtk/Widget.md#.notifyInputChanged), [setOnInputChanged()](../lwtk/Widget.md#.setOnInputChanged), [setOnRealize()](../lwtk/Widget.md#.setOnRealize), [_setApp()](../lwtk/Widget.md#._setApp), [_setParent()](../lwtk/Widget.md#._setParent)
+ * [Animatable](../lwtk/Animatable.md):
+ * [animateFrame()](../lwtk/Animatable.md#.animateFrame), [getStyle()](../lwtk/Animatable.md#.getStyle), [getStyleParam()](../lwtk/Animatable.md#.getStyleParam), [isVisible()](../lwtk/Animatable.md#.isVisible), [setState()](../lwtk/Animatable.md#.setState), [setStates()](../lwtk/Animatable.md#.setStates), [setStyle()](../lwtk/Animatable.md#.setStyle), [setVisible()](../lwtk/Animatable.md#.setVisible), [updateAnimation()](../lwtk/Animatable.md#.updateAnimation), [_setStyleFromParent()](../lwtk/Animatable.md#._setStyleFromParent)
+ * [Styleable](../lwtk/Styleable.md):
+ * [clearStyleCache()](../lwtk/Styleable.md#.clearStyleCache), [getStateString()](../lwtk/Styleable.md#.getStateString), [_getStyleParam()](../lwtk/Styleable.md#._getStyleParam)
+ * **[Component](../lwtk/Component.md)**:
+ * [byId()](../lwtk/Component.md#.byId), [getCurrentTime()](../lwtk/Component.md#.getCurrentTime), [getFocusHandler()](../lwtk/Component.md#.getFocusHandler), [getFontInfo()](../lwtk/Component.md#.getFontInfo), [getFrame()](../lwtk/Component.md#.getFrame), [getLayoutContext()](../lwtk/Component.md#.getLayoutContext), [getParent()](../lwtk/Component.md#.getParent), [getRoot()](../lwtk/Component.md#.getRoot), [getSize()](../lwtk/Component.md#.getSize), [handleRemainingInitParams()](../lwtk/Component.md#.handleRemainingInitParams), [parentById()](../lwtk/Component.md#.parentById), [setFrame()](../lwtk/Component.md#.setFrame), [setInitParams()](../lwtk/Component.md#.setInitParams), [setTimer()](../lwtk/Component.md#.setTimer), [transformXY()](../lwtk/Component.md#.transformXY), [triggerLayout()](../lwtk/Component.md#.triggerLayout), [triggerRedraw()](../lwtk/Component.md#.triggerRedraw), [updateFrameTransition()](../lwtk/Component.md#.updateFrameTransition), [_setFrame()](../lwtk/Component.md#._setFrame)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam), [_processMouseDown()](../lwtk/Drawable.md#._processMouseDown), [_processMouseEnter()](../lwtk/Drawable.md#._processMouseEnter), [_processMouseLeave()](../lwtk/Drawable.md#._processMouseLeave), [_processMouseMove()](../lwtk/Drawable.md#._processMouseMove), [_processMouseScroll()](../lwtk/Drawable.md#._processMouseScroll), [_processMouseUp()](../lwtk/Drawable.md#._processMouseUp)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/Rect.md b/doc/gen/lwtk/Rect.md
new file mode 100644
index 0000000..3617db5
--- /dev/null
+++ b/doc/gen/lwtk/Rect.md
@@ -0,0 +1,55 @@
+# Class lwtk.Rect
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [contains()](#.contains)
+ * [intersects()](#.intersects)
+ * [toXYWH()](#.toXYWH)
+ * [Functions](#functions)
+ * [areRectsIntersected()](#.areRectsIntersected)
+ * [doesRectContain()](#.doesRectContain)
+ * [intersectRects()](#.intersectRects)
+ * [round()](#.round)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / _**`Rect`**_
+
+## Constructor
+ * **`Rect(x, y, w, h)`**
+
+
+
+## Methods
+ * **`Rect:contains(x, y, w, h)`**
+
+
+ * **`Rect:intersects(x, y, w, h)`**
+
+
+ * **`Rect:toXYWH()`**
+
+
+
+## Functions
+ * **`Rect.areRectsIntersected(x1, y1, w1, h1, x2, y2, w2, h2)`**
+
+
+ * **`Rect.doesRectContain(x1, y1, w1, h1, x2, y2, w2, h2)`**
+
+
+ * **`Rect.intersectRects(x1, y1, w1, h1, x2, y2, w2, h2)`**
+
+
+ * **`Rect.round(x, y, w, h)`**
+
+
+
+## Inherited Methods
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/Row.md b/doc/gen/lwtk/Row.md
new file mode 100644
index 0000000..14d4ab6
--- /dev/null
+++ b/doc/gen/lwtk/Row.md
@@ -0,0 +1,55 @@
+# Class lwtk.Row
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [getMeasures()](#.getMeasures)
+ * [onLayout()](#.onLayout)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) / [MouseDispatcher](../lwtk/MouseDispatcher.md#inheritance) / **[Group](../lwtk/Group.md#inheritance)** / _**`Row`**_
+
+## Constructor
+ * **`Row(initParams)`**
+
+ * Inherited from: **[Group()](../lwtk/Group.md#constructor)**
+
+
+## Methods
+ * **`Row:getMeasures()`**
+
+ * Implements: [Component:getMeasures()](../lwtk/Component.md#.getMeasures)
+
+
+ * **`Row:onLayout(width, height, isLayoutTransition)`**
+
+ * Implements: [Component:onLayout()](../lwtk/Component.md#.onLayout)
+
+
+
+## Inherited Methods
+ * **[Group](../lwtk/Group.md)**:
+ * [addChild()](../lwtk/Group.md#.addChild), [childById()](../lwtk/Group.md#.childById), [_clearChildLookup()](../lwtk/Group.md#._clearChildLookup)
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md):
+ * [_processMouseDown()](../lwtk/MouseDispatcher.md#._processMouseDown), [_processMouseEnter()](../lwtk/MouseDispatcher.md#._processMouseEnter), [_processMouseLeave()](../lwtk/MouseDispatcher.md#._processMouseLeave), [_processMouseMove()](../lwtk/MouseDispatcher.md#._processMouseMove), [_processMouseScroll()](../lwtk/MouseDispatcher.md#._processMouseScroll), [_processMouseUp()](../lwtk/MouseDispatcher.md#._processMouseUp)
+ * [Compound](../lwtk/Compound.md):
+ * [_processChanges()](../lwtk/Compound.md#._processChanges), [_processDraw()](../lwtk/Compound.md#._processDraw)
+ * **[Widget](../lwtk/Widget.md)**:
+ * [notifyInputChanged()](../lwtk/Widget.md#.notifyInputChanged), [setOnInputChanged()](../lwtk/Widget.md#.setOnInputChanged), [setOnRealize()](../lwtk/Widget.md#.setOnRealize), [_setApp()](../lwtk/Widget.md#._setApp), [_setParent()](../lwtk/Widget.md#._setParent)
+ * [Animatable](../lwtk/Animatable.md):
+ * [animateFrame()](../lwtk/Animatable.md#.animateFrame), [getStyle()](../lwtk/Animatable.md#.getStyle), [getStyleParam()](../lwtk/Animatable.md#.getStyleParam), [isVisible()](../lwtk/Animatable.md#.isVisible), [setState()](../lwtk/Animatable.md#.setState), [setStates()](../lwtk/Animatable.md#.setStates), [setStyle()](../lwtk/Animatable.md#.setStyle), [setVisible()](../lwtk/Animatable.md#.setVisible), [updateAnimation()](../lwtk/Animatable.md#.updateAnimation), [_setStyleFromParent()](../lwtk/Animatable.md#._setStyleFromParent)
+ * [Styleable](../lwtk/Styleable.md):
+ * [clearStyleCache()](../lwtk/Styleable.md#.clearStyleCache), [getStateString()](../lwtk/Styleable.md#.getStateString), [_getStyleParam()](../lwtk/Styleable.md#._getStyleParam)
+ * **[Component](../lwtk/Component.md)**:
+ * [byId()](../lwtk/Component.md#.byId), [getCurrentTime()](../lwtk/Component.md#.getCurrentTime), [getFocusHandler()](../lwtk/Component.md#.getFocusHandler), [getFontInfo()](../lwtk/Component.md#.getFontInfo), [getFrame()](../lwtk/Component.md#.getFrame), [getLayoutContext()](../lwtk/Component.md#.getLayoutContext), [getParent()](../lwtk/Component.md#.getParent), [getRoot()](../lwtk/Component.md#.getRoot), [getSize()](../lwtk/Component.md#.getSize), [handleRemainingInitParams()](../lwtk/Component.md#.handleRemainingInitParams), [parentById()](../lwtk/Component.md#.parentById), [setFrame()](../lwtk/Component.md#.setFrame), [setInitParams()](../lwtk/Component.md#.setInitParams), [setTimer()](../lwtk/Component.md#.setTimer), [transformXY()](../lwtk/Component.md#.transformXY), [triggerLayout()](../lwtk/Component.md#.triggerLayout), [triggerRedraw()](../lwtk/Component.md#.triggerRedraw), [updateFrameTransition()](../lwtk/Component.md#.updateFrameTransition), [_setFrame()](../lwtk/Component.md#._setFrame)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/Space.md b/doc/gen/lwtk/Space.md
new file mode 100644
index 0000000..bc44710
--- /dev/null
+++ b/doc/gen/lwtk/Space.md
@@ -0,0 +1,67 @@
+# Class lwtk.Space
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [addChild()](#.addChild)
+ * [getMeasures()](#.getMeasures)
+ * [onLayout()](#.onLayout)
+ * [setUnlimited()](#.setUnlimited)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) / [MouseDispatcher](../lwtk/MouseDispatcher.md#inheritance) / **[Group](../lwtk/Group.md#inheritance)** / _**`Space`**_
+
+## Constructor
+ * **`Space(initParams)`**
+
+ * Inherited from: **[Group()](../lwtk/Group.md#constructor)**
+
+
+## Methods
+ * **`Space:addChild(child)`**
+
+ * Overrides: [Group:addChild()](../lwtk/Group.md#.addChild)
+ * Overrides: [Compound:addChild()](../lwtk/Compound.md#.addChild)
+ * Implements: [Drawable:addChild()](../lwtk/Drawable.md#.addChild)
+
+
+ * **`Space:getMeasures()`**
+
+ * Implements: [Component:getMeasures()](../lwtk/Component.md#.getMeasures)
+
+
+ * **`Space:onLayout(w, h)`**
+
+ * Implements: [Component:onLayout()](../lwtk/Component.md#.onLayout)
+
+
+ * **`Space:setUnlimited(unlimited)`**
+
+
+
+## Inherited Methods
+ * **[Group](../lwtk/Group.md)**:
+ * [childById()](../lwtk/Group.md#.childById), [_clearChildLookup()](../lwtk/Group.md#._clearChildLookup)
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md):
+ * [_processMouseDown()](../lwtk/MouseDispatcher.md#._processMouseDown), [_processMouseEnter()](../lwtk/MouseDispatcher.md#._processMouseEnter), [_processMouseLeave()](../lwtk/MouseDispatcher.md#._processMouseLeave), [_processMouseMove()](../lwtk/MouseDispatcher.md#._processMouseMove), [_processMouseScroll()](../lwtk/MouseDispatcher.md#._processMouseScroll), [_processMouseUp()](../lwtk/MouseDispatcher.md#._processMouseUp)
+ * [Compound](../lwtk/Compound.md):
+ * [_processChanges()](../lwtk/Compound.md#._processChanges), [_processDraw()](../lwtk/Compound.md#._processDraw)
+ * **[Widget](../lwtk/Widget.md)**:
+ * [notifyInputChanged()](../lwtk/Widget.md#.notifyInputChanged), [setOnInputChanged()](../lwtk/Widget.md#.setOnInputChanged), [setOnRealize()](../lwtk/Widget.md#.setOnRealize), [_setApp()](../lwtk/Widget.md#._setApp), [_setParent()](../lwtk/Widget.md#._setParent)
+ * [Animatable](../lwtk/Animatable.md):
+ * [animateFrame()](../lwtk/Animatable.md#.animateFrame), [getStyle()](../lwtk/Animatable.md#.getStyle), [getStyleParam()](../lwtk/Animatable.md#.getStyleParam), [isVisible()](../lwtk/Animatable.md#.isVisible), [setState()](../lwtk/Animatable.md#.setState), [setStates()](../lwtk/Animatable.md#.setStates), [setStyle()](../lwtk/Animatable.md#.setStyle), [setVisible()](../lwtk/Animatable.md#.setVisible), [updateAnimation()](../lwtk/Animatable.md#.updateAnimation), [_setStyleFromParent()](../lwtk/Animatable.md#._setStyleFromParent)
+ * [Styleable](../lwtk/Styleable.md):
+ * [clearStyleCache()](../lwtk/Styleable.md#.clearStyleCache), [getStateString()](../lwtk/Styleable.md#.getStateString), [_getStyleParam()](../lwtk/Styleable.md#._getStyleParam)
+ * **[Component](../lwtk/Component.md)**:
+ * [byId()](../lwtk/Component.md#.byId), [getCurrentTime()](../lwtk/Component.md#.getCurrentTime), [getFocusHandler()](../lwtk/Component.md#.getFocusHandler), [getFontInfo()](../lwtk/Component.md#.getFontInfo), [getFrame()](../lwtk/Component.md#.getFrame), [getLayoutContext()](../lwtk/Component.md#.getLayoutContext), [getParent()](../lwtk/Component.md#.getParent), [getRoot()](../lwtk/Component.md#.getRoot), [getSize()](../lwtk/Component.md#.getSize), [handleRemainingInitParams()](../lwtk/Component.md#.handleRemainingInitParams), [parentById()](../lwtk/Component.md#.parentById), [setFrame()](../lwtk/Component.md#.setFrame), [setInitParams()](../lwtk/Component.md#.setInitParams), [setTimer()](../lwtk/Component.md#.setTimer), [transformXY()](../lwtk/Component.md#.transformXY), [triggerLayout()](../lwtk/Component.md#.triggerLayout), [triggerRedraw()](../lwtk/Component.md#.triggerRedraw), [updateFrameTransition()](../lwtk/Component.md#.updateFrameTransition), [_setFrame()](../lwtk/Component.md#._setFrame)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/Square.md b/doc/gen/lwtk/Square.md
new file mode 100644
index 0000000..587c57a
--- /dev/null
+++ b/doc/gen/lwtk/Square.md
@@ -0,0 +1,63 @@
+# Class lwtk.Square
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [addChild()](#.addChild)
+ * [getMeasures()](#.getMeasures)
+ * [onLayout()](#.onLayout)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) / [MouseDispatcher](../lwtk/MouseDispatcher.md#inheritance) / **[Group](../lwtk/Group.md#inheritance)** / _**`Square`**_
+
+## Constructor
+ * **`Square(initParams)`**
+
+ * Inherited from: **[Group()](../lwtk/Group.md#constructor)**
+
+
+## Methods
+ * **`Square:addChild(child)`**
+
+ * Overrides: [Group:addChild()](../lwtk/Group.md#.addChild)
+ * Overrides: [Compound:addChild()](../lwtk/Compound.md#.addChild)
+ * Implements: [Drawable:addChild()](../lwtk/Drawable.md#.addChild)
+
+
+ * **`Square:getMeasures()`**
+
+ * Implements: [Component:getMeasures()](../lwtk/Component.md#.getMeasures)
+
+
+ * **`Square:onLayout(w, h)`**
+
+ * Implements: [Component:onLayout()](../lwtk/Component.md#.onLayout)
+
+
+
+## Inherited Methods
+ * **[Group](../lwtk/Group.md)**:
+ * [childById()](../lwtk/Group.md#.childById), [_clearChildLookup()](../lwtk/Group.md#._clearChildLookup)
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md):
+ * [_processMouseDown()](../lwtk/MouseDispatcher.md#._processMouseDown), [_processMouseEnter()](../lwtk/MouseDispatcher.md#._processMouseEnter), [_processMouseLeave()](../lwtk/MouseDispatcher.md#._processMouseLeave), [_processMouseMove()](../lwtk/MouseDispatcher.md#._processMouseMove), [_processMouseScroll()](../lwtk/MouseDispatcher.md#._processMouseScroll), [_processMouseUp()](../lwtk/MouseDispatcher.md#._processMouseUp)
+ * [Compound](../lwtk/Compound.md):
+ * [_processChanges()](../lwtk/Compound.md#._processChanges), [_processDraw()](../lwtk/Compound.md#._processDraw)
+ * **[Widget](../lwtk/Widget.md)**:
+ * [notifyInputChanged()](../lwtk/Widget.md#.notifyInputChanged), [setOnInputChanged()](../lwtk/Widget.md#.setOnInputChanged), [setOnRealize()](../lwtk/Widget.md#.setOnRealize), [_setApp()](../lwtk/Widget.md#._setApp), [_setParent()](../lwtk/Widget.md#._setParent)
+ * [Animatable](../lwtk/Animatable.md):
+ * [animateFrame()](../lwtk/Animatable.md#.animateFrame), [getStyle()](../lwtk/Animatable.md#.getStyle), [getStyleParam()](../lwtk/Animatable.md#.getStyleParam), [isVisible()](../lwtk/Animatable.md#.isVisible), [setState()](../lwtk/Animatable.md#.setState), [setStates()](../lwtk/Animatable.md#.setStates), [setStyle()](../lwtk/Animatable.md#.setStyle), [setVisible()](../lwtk/Animatable.md#.setVisible), [updateAnimation()](../lwtk/Animatable.md#.updateAnimation), [_setStyleFromParent()](../lwtk/Animatable.md#._setStyleFromParent)
+ * [Styleable](../lwtk/Styleable.md):
+ * [clearStyleCache()](../lwtk/Styleable.md#.clearStyleCache), [getStateString()](../lwtk/Styleable.md#.getStateString), [_getStyleParam()](../lwtk/Styleable.md#._getStyleParam)
+ * **[Component](../lwtk/Component.md)**:
+ * [byId()](../lwtk/Component.md#.byId), [getCurrentTime()](../lwtk/Component.md#.getCurrentTime), [getFocusHandler()](../lwtk/Component.md#.getFocusHandler), [getFontInfo()](../lwtk/Component.md#.getFontInfo), [getFrame()](../lwtk/Component.md#.getFrame), [getLayoutContext()](../lwtk/Component.md#.getLayoutContext), [getParent()](../lwtk/Component.md#.getParent), [getRoot()](../lwtk/Component.md#.getRoot), [getSize()](../lwtk/Component.md#.getSize), [handleRemainingInitParams()](../lwtk/Component.md#.handleRemainingInitParams), [parentById()](../lwtk/Component.md#.parentById), [setFrame()](../lwtk/Component.md#.setFrame), [setInitParams()](../lwtk/Component.md#.setInitParams), [setTimer()](../lwtk/Component.md#.setTimer), [transformXY()](../lwtk/Component.md#.transformXY), [triggerLayout()](../lwtk/Component.md#.triggerLayout), [triggerRedraw()](../lwtk/Component.md#.triggerRedraw), [updateFrameTransition()](../lwtk/Component.md#.updateFrameTransition), [_setFrame()](../lwtk/Component.md#._setFrame)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/Style.md b/doc/gen/lwtk/Style.md
new file mode 100644
index 0000000..1e9a897
--- /dev/null
+++ b/doc/gen/lwtk/Style.md
@@ -0,0 +1,77 @@
+# Class lwtk.Style
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [addRules()](#.addRules)
+ * [clearCache()](#.clearCache)
+ * [getScaleFactor()](#.getScaleFactor)
+ * [getStyleParam()](#.getStyleParam)
+ * [isAnimatable()](#.isAnimatable)
+ * [isScalable()](#.isScalable)
+ * [setRules()](#.setRules)
+ * [setScaleFactor()](#.setScaleFactor)
+ * [_getStyleParam()](#._getStyleParam)
+ * [_getStyleParam2()](#._getStyleParam2)
+ * [_replaceParentStyle()](#._replaceParentStyle)
+ * [_setParentStyle()](#._setParentStyle)
+ * [Inherited Methods](#inherited-methods)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / _**`Style`**_
+
+## Constructor
+ * **`Style(ruleList)`**
+
+
+
+## Methods
+ * **`Style:addRules(rules)`**
+
+
+ * **`Style:clearCache()`**
+
+
+ * **`Style:getScaleFactor()`**
+
+
+ * **`Style:getStyleParam(widget, parName)`**
+
+
+ * **`Style:isAnimatable(parName)`**
+
+
+ * **`Style:isScalable(parName)`**
+
+
+ * **`Style:setRules(rules)`**
+
+
+ * **`Style:setScaleFactor(scaleFactor)`**
+
+
+ * **`Style:_getStyleParam(parName, classSelectorPath, stateSelectorPath, considerLocal, extraParentStyle)`**
+
+
+ * **`Style:_getStyleParam2(parName, classSelectorPath, stateSelectorPath, ctxRules)`**
+
+
+ * **`Style:_replaceParentStyle(parentStyle)`**
+
+
+ * **`Style:_setParentStyle(parentStyle)`**
+
+
+
+## Inherited Methods
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / _**`Style`**_ / **[DefaultStyle](../lwtk/DefaultStyle.md#inheritance)**
+
diff --git a/doc/gen/lwtk/StyleRef.md b/doc/gen/lwtk/StyleRef.md
new file mode 100644
index 0000000..b703e38
--- /dev/null
+++ b/doc/gen/lwtk/StyleRef.md
@@ -0,0 +1,3 @@
+# Table lwtk.StyleRef
+
+
diff --git a/doc/gen/lwtk/StyleTypeAttributes.md b/doc/gen/lwtk/StyleTypeAttributes.md
new file mode 100644
index 0000000..1b43b95
--- /dev/null
+++ b/doc/gen/lwtk/StyleTypeAttributes.md
@@ -0,0 +1,3 @@
+# Table lwtk.StyleTypeAttributes
+
+
diff --git a/doc/gen/lwtk/Styleable.md b/doc/gen/lwtk/Styleable.md
new file mode 100644
index 0000000..a673f48
--- /dev/null
+++ b/doc/gen/lwtk/Styleable.md
@@ -0,0 +1,32 @@
+# Mixin lwtk.Styleable
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) /
+ * **[Component](../lwtk/Component.md#inheritance)** / _`Styleable`_
+ * _`Styleable`_
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / [Actionable](../lwtk/Actionable.md#subclasses) / [Node](../lwtk/Node.md#subclasses) / [Drawable](../lwtk/Drawable.md#subclasses) /
+ * **[Component](../lwtk/Component.md#subclasses)** / _`Styleable`_ / [Animatable](../lwtk/Animatable.md#subclasses) / **[Widget](../lwtk/Widget.md#subclasses)** / [Compound](../lwtk/Compound.md#subclasses) /
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[TextInput](../lwtk/TextInput.md#inheritance)**
+ * [HotkeyListener](../lwtk/HotkeyListener.md#subclasses) / **[Button](../lwtk/Button.md#subclasses)** /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[PushButton](../lwtk/PushButton.md#inheritance)**
+ * **[TextLabel](../lwtk/TextLabel.md#subclasses)** / **[TitleText](../lwtk/TitleText.md#inheritance)**
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Group](../lwtk/Group.md#subclasses)** /
+ * [Colored](../lwtk/Colored.md#subclasses) / **[ViewSwitcher](../lwtk/ViewSwitcher.md#inheritance)**
+ * **[Column](../lwtk/Column.md#inheritance)**
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) / **[Box](../lwtk/Box.md#subclasses)** / [Focusable](../lwtk/Focusable.md#subclasses) / **[FocusGroup](../lwtk/FocusGroup.md#inheritance)**
+ * **[Matrix](../lwtk/Matrix.md#inheritance)**
+ * **[Row](../lwtk/Row.md#inheritance)**
+ * **[Space](../lwtk/Space.md#inheritance)**
+ * **[Square](../lwtk/Square.md#inheritance)**
+ * _`Styleable`_ / [KeyHandler](../lwtk/KeyHandler.md#subclasses) / [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Window](../lwtk/Window.md#inheritance)**
+
diff --git a/doc/gen/lwtk/TextCursor.md b/doc/gen/lwtk/TextCursor.md
new file mode 100644
index 0000000..925f11d
--- /dev/null
+++ b/doc/gen/lwtk/TextCursor.md
@@ -0,0 +1,37 @@
+# Class lwtk.TextCursor
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [onDraw()](#.onDraw)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / _**`TextCursor`**_
+
+## Constructor
+ * **`TextCursor(initParams)`**
+
+ * Inherited from: **[Component()](../lwtk/Component.md#constructor)**
+
+
+## Methods
+ * **`TextCursor:onDraw(ctx)`**
+
+ * Implements: [Component:onDraw()](../lwtk/Component.md#.onDraw)
+
+
+
+## Inherited Methods
+ * **[Component](../lwtk/Component.md)**:
+ * [animateFrame()](../lwtk/Component.md#.animateFrame), [byId()](../lwtk/Component.md#.byId), [getCurrentTime()](../lwtk/Component.md#.getCurrentTime), [getFocusHandler()](../lwtk/Component.md#.getFocusHandler), [getFontInfo()](../lwtk/Component.md#.getFontInfo), [getFrame()](../lwtk/Component.md#.getFrame), [getLayoutContext()](../lwtk/Component.md#.getLayoutContext), [getParent()](../lwtk/Component.md#.getParent), [getRoot()](../lwtk/Component.md#.getRoot), [getSize()](../lwtk/Component.md#.getSize), [handleRemainingInitParams()](../lwtk/Component.md#.handleRemainingInitParams), [parentById()](../lwtk/Component.md#.parentById), [setFrame()](../lwtk/Component.md#.setFrame), [setInitParams()](../lwtk/Component.md#.setInitParams), [setTimer()](../lwtk/Component.md#.setTimer), [transformXY()](../lwtk/Component.md#.transformXY), [triggerLayout()](../lwtk/Component.md#.triggerLayout), [triggerRedraw()](../lwtk/Component.md#.triggerRedraw), [updateAnimation()](../lwtk/Component.md#.updateAnimation), [updateFrameTransition()](../lwtk/Component.md#.updateFrameTransition), [_processChanges()](../lwtk/Component.md#._processChanges), [_processDraw()](../lwtk/Component.md#._processDraw), [_setApp()](../lwtk/Component.md#._setApp), [_setFrame()](../lwtk/Component.md#._setFrame), [_setParent()](../lwtk/Component.md#._setParent)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam), [getStyleParam()](../lwtk/Drawable.md#.getStyleParam), [_processMouseDown()](../lwtk/Drawable.md#._processMouseDown), [_processMouseEnter()](../lwtk/Drawable.md#._processMouseEnter), [_processMouseLeave()](../lwtk/Drawable.md#._processMouseLeave), [_processMouseMove()](../lwtk/Drawable.md#._processMouseMove), [_processMouseScroll()](../lwtk/Drawable.md#._processMouseScroll), [_processMouseUp()](../lwtk/Drawable.md#._processMouseUp)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/TextFragment.md b/doc/gen/lwtk/TextFragment.md
new file mode 100644
index 0000000..69ae57c
--- /dev/null
+++ b/doc/gen/lwtk/TextFragment.md
@@ -0,0 +1,65 @@
+# Class lwtk.TextFragment
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [getFontInfo()](#.getFontInfo)
+ * [getTextMeasures()](#.getTextMeasures)
+ * [onDraw()](#.onDraw)
+ * [setConsiderHotkey()](#.setConsiderHotkey)
+ * [setShowHotkey()](#.setShowHotkey)
+ * [setText()](#.setText)
+ * [setTextPos()](#.setTextPos)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / _**`TextFragment`**_
+
+## Constructor
+ * **`TextFragment(initParams)`**
+
+ * Overrides: **[Component()](../lwtk/Component.md#constructor)**
+ * Overrides: [Actionable()](../lwtk/Actionable.md#constructor)
+
+
+
+## Methods
+ * **`TextFragment:getFontInfo(family, slant, weight, size)`**
+
+ * Overrides: [Component:getFontInfo()](../lwtk/Component.md#.getFontInfo)
+
+
+ * **`TextFragment:getTextMeasures()`**
+
+
+ * **`TextFragment:onDraw(ctx, ...)`**
+
+ * Implements: [Component:onDraw()](../lwtk/Component.md#.onDraw)
+
+
+ * **`TextFragment:setConsiderHotkey(flag)`**
+
+
+ * **`TextFragment:setShowHotkey(flag)`**
+
+
+ * **`TextFragment:setText(text)`**
+
+
+ * **`TextFragment:setTextPos(tx, ty)`**
+
+
+
+## Inherited Methods
+ * **[Component](../lwtk/Component.md)**:
+ * [animateFrame()](../lwtk/Component.md#.animateFrame), [byId()](../lwtk/Component.md#.byId), [getCurrentTime()](../lwtk/Component.md#.getCurrentTime), [getFocusHandler()](../lwtk/Component.md#.getFocusHandler), [getFrame()](../lwtk/Component.md#.getFrame), [getLayoutContext()](../lwtk/Component.md#.getLayoutContext), [getParent()](../lwtk/Component.md#.getParent), [getRoot()](../lwtk/Component.md#.getRoot), [getSize()](../lwtk/Component.md#.getSize), [handleRemainingInitParams()](../lwtk/Component.md#.handleRemainingInitParams), [parentById()](../lwtk/Component.md#.parentById), [setFrame()](../lwtk/Component.md#.setFrame), [setInitParams()](../lwtk/Component.md#.setInitParams), [setTimer()](../lwtk/Component.md#.setTimer), [transformXY()](../lwtk/Component.md#.transformXY), [triggerLayout()](../lwtk/Component.md#.triggerLayout), [triggerRedraw()](../lwtk/Component.md#.triggerRedraw), [updateAnimation()](../lwtk/Component.md#.updateAnimation), [updateFrameTransition()](../lwtk/Component.md#.updateFrameTransition), [_processChanges()](../lwtk/Component.md#._processChanges), [_processDraw()](../lwtk/Component.md#._processDraw), [_setApp()](../lwtk/Component.md#._setApp), [_setFrame()](../lwtk/Component.md#._setFrame), [_setParent()](../lwtk/Component.md#._setParent)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam), [getStyleParam()](../lwtk/Drawable.md#.getStyleParam), [_processMouseDown()](../lwtk/Drawable.md#._processMouseDown), [_processMouseEnter()](../lwtk/Drawable.md#._processMouseEnter), [_processMouseLeave()](../lwtk/Drawable.md#._processMouseLeave), [_processMouseMove()](../lwtk/Drawable.md#._processMouseMove), [_processMouseScroll()](../lwtk/Drawable.md#._processMouseScroll), [_processMouseUp()](../lwtk/Drawable.md#._processMouseUp)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/TextInput.md b/doc/gen/lwtk/TextInput.md
new file mode 100644
index 0000000..be44082
--- /dev/null
+++ b/doc/gen/lwtk/TextInput.md
@@ -0,0 +1,136 @@
+# Class lwtk.TextInput
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [getMeasures()](#.getMeasures)
+ * [onActionCursorLeft()](#.onActionCursorLeft)
+ * [onActionCursorRight()](#.onActionCursorRight)
+ * [onActionCursorToBeginOfLine()](#.onActionCursorToBeginOfLine)
+ * [onActionCursorToEndOfLine()](#.onActionCursorToEndOfLine)
+ * [onActionDeleteCharLeft()](#.onActionDeleteCharLeft)
+ * [onActionDeleteCharRight()](#.onActionDeleteCharRight)
+ * [onActionInputNext()](#.onActionInputNext)
+ * [onActionInputPrev()](#.onActionInputPrev)
+ * [onFocusIn()](#.onFocusIn)
+ * [onFocusOut()](#.onFocusOut)
+ * [onKeyDown()](#.onKeyDown)
+ * [onLayout()](#.onLayout)
+ * [onMouseDown()](#.onMouseDown)
+ * [onRealize()](#.onRealize)
+ * [setDefault()](#.setDefault)
+ * [setFilterInput()](#.setFilterInput)
+ * [setText()](#.setText)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) / [LayoutFrame](../lwtk/LayoutFrame.md#inheritance) / [Control](../lwtk/Control.md#inheritance) / [Focusable](../lwtk/Focusable.md#inheritance) / _**`TextInput`**_
+
+## Constructor
+ * **`TextInput(initParams)`**
+
+ * Overrides: **[Widget()](../lwtk/Widget.md#constructor)**
+ * Overrides: [Animatable()](../lwtk/Animatable.md#constructor)
+ * Overrides: [Styleable()](../lwtk/Styleable.md#constructor)
+ * Overrides: **[Component()](../lwtk/Component.md#constructor)**
+ * Overrides: [Actionable()](../lwtk/Actionable.md#constructor)
+
+
+
+## Methods
+ * **`TextInput:getMeasures()`**
+
+ * Implementation: [TextLabel:getMeasures()](../lwtk/TextLabel.md#getMeasures)
+ * Implements: [Component:getMeasures()](../lwtk/Component.md#.getMeasures)
+
+
+ * **`TextInput:onActionCursorLeft()`**
+
+
+ * **`TextInput:onActionCursorRight()`**
+
+
+ * **`TextInput:onActionCursorToBeginOfLine()`**
+
+
+ * **`TextInput:onActionCursorToEndOfLine()`**
+
+
+ * **`TextInput:onActionDeleteCharLeft()`**
+
+
+ * **`TextInput:onActionDeleteCharRight()`**
+
+
+ * **`TextInput:onActionInputNext()`**
+
+
+ * **`TextInput:onActionInputPrev()`**
+
+
+ * **`TextInput:onFocusIn()`**
+
+ * Implements: [Focusable:onFocusIn()](../lwtk/Focusable.md#.onFocusIn)
+
+
+ * **`TextInput:onFocusOut()`**
+
+ * Implements: [Focusable:onFocusOut()](../lwtk/Focusable.md#.onFocusOut)
+
+
+ * **`TextInput:onKeyDown(keyName, keyState, keyText)`**
+
+ * Implements: [Focusable:onKeyDown()](../lwtk/Focusable.md#.onKeyDown)
+
+
+ * **`TextInput:onLayout(width, height)`**
+
+ * Overrides: [LayoutFrame:onLayout()](../lwtk/LayoutFrame.md#.onLayout)
+ * Implements: [Component:onLayout()](../lwtk/Component.md#.onLayout)
+
+
+ * **`TextInput:onMouseDown(mx, my, button, modState)`**
+
+ * Implements: [Node:onMouseDown()](../lwtk/Node.md#.onMouseDown)
+
+
+ * **`TextInput:onRealize()`**
+
+ * Implements: [Component:onRealize()](../lwtk/Component.md#.onRealize)
+
+
+ * **`TextInput:setDefault(buttonId)`**
+
+
+ * **`TextInput:setFilterInput(filterInput)`**
+
+
+ * **`TextInput:setText(text)`**
+
+
+
+## Inherited Methods
+ * [Focusable](../lwtk/Focusable.md):
+ * [onDisabled()](../lwtk/Focusable.md#.onDisabled), [onEffectiveVisibilityChanged()](../lwtk/Focusable.md#.onEffectiveVisibilityChanged), [setFocus()](../lwtk/Focusable.md#.setFocus), [_handleFocusIn()](../lwtk/Focusable.md#._handleFocusIn), [_handleFocusOut()](../lwtk/Focusable.md#._handleFocusOut), [_handleHasFocusHandler()](../lwtk/Focusable.md#._handleHasFocusHandler)
+ * [LayoutFrame](../lwtk/LayoutFrame.md):
+ * [addChild()](../lwtk/LayoutFrame.md#.addChild), [onDraw()](../lwtk/LayoutFrame.md#.onDraw)
+ * [Compound](../lwtk/Compound.md):
+ * [_processChanges()](../lwtk/Compound.md#._processChanges), [_processDraw()](../lwtk/Compound.md#._processDraw)
+ * **[Widget](../lwtk/Widget.md)**:
+ * [notifyInputChanged()](../lwtk/Widget.md#.notifyInputChanged), [setOnInputChanged()](../lwtk/Widget.md#.setOnInputChanged), [setOnRealize()](../lwtk/Widget.md#.setOnRealize), [_setApp()](../lwtk/Widget.md#._setApp), [_setParent()](../lwtk/Widget.md#._setParent)
+ * [Animatable](../lwtk/Animatable.md):
+ * [animateFrame()](../lwtk/Animatable.md#.animateFrame), [getStyle()](../lwtk/Animatable.md#.getStyle), [getStyleParam()](../lwtk/Animatable.md#.getStyleParam), [isVisible()](../lwtk/Animatable.md#.isVisible), [setState()](../lwtk/Animatable.md#.setState), [setStates()](../lwtk/Animatable.md#.setStates), [setStyle()](../lwtk/Animatable.md#.setStyle), [setVisible()](../lwtk/Animatable.md#.setVisible), [updateAnimation()](../lwtk/Animatable.md#.updateAnimation), [_setStyleFromParent()](../lwtk/Animatable.md#._setStyleFromParent)
+ * [Styleable](../lwtk/Styleable.md):
+ * [clearStyleCache()](../lwtk/Styleable.md#.clearStyleCache), [getStateString()](../lwtk/Styleable.md#.getStateString), [_getStyleParam()](../lwtk/Styleable.md#._getStyleParam)
+ * **[Component](../lwtk/Component.md)**:
+ * [byId()](../lwtk/Component.md#.byId), [getCurrentTime()](../lwtk/Component.md#.getCurrentTime), [getFocusHandler()](../lwtk/Component.md#.getFocusHandler), [getFontInfo()](../lwtk/Component.md#.getFontInfo), [getFrame()](../lwtk/Component.md#.getFrame), [getLayoutContext()](../lwtk/Component.md#.getLayoutContext), [getParent()](../lwtk/Component.md#.getParent), [getRoot()](../lwtk/Component.md#.getRoot), [getSize()](../lwtk/Component.md#.getSize), [handleRemainingInitParams()](../lwtk/Component.md#.handleRemainingInitParams), [parentById()](../lwtk/Component.md#.parentById), [setFrame()](../lwtk/Component.md#.setFrame), [setInitParams()](../lwtk/Component.md#.setInitParams), [setTimer()](../lwtk/Component.md#.setTimer), [transformXY()](../lwtk/Component.md#.transformXY), [triggerLayout()](../lwtk/Component.md#.triggerLayout), [triggerRedraw()](../lwtk/Component.md#.triggerRedraw), [updateFrameTransition()](../lwtk/Component.md#.updateFrameTransition), [_setFrame()](../lwtk/Component.md#._setFrame)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam), [_processMouseDown()](../lwtk/Drawable.md#._processMouseDown), [_processMouseEnter()](../lwtk/Drawable.md#._processMouseEnter), [_processMouseLeave()](../lwtk/Drawable.md#._processMouseLeave), [_processMouseMove()](../lwtk/Drawable.md#._processMouseMove), [_processMouseScroll()](../lwtk/Drawable.md#._processMouseScroll), [_processMouseUp()](../lwtk/Drawable.md#._processMouseUp)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/TextLabel.md b/doc/gen/lwtk/TextLabel.md
new file mode 100644
index 0000000..a78c724
--- /dev/null
+++ b/doc/gen/lwtk/TextLabel.md
@@ -0,0 +1,97 @@
+# Class lwtk.TextLabel
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [getMeasures()](#.getMeasures)
+ * [onHotkeyDisabled()](#.onHotkeyDisabled)
+ * [onHotkeyDown()](#.onHotkeyDown)
+ * [onHotkeyEnabled()](#.onHotkeyEnabled)
+ * [onLayout()](#.onLayout)
+ * [onMouseDown()](#.onMouseDown)
+ * [setInput()](#.setInput)
+ * [setText()](#.setText)
+ * [Inherited Methods](#inherited-methods)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) / [LayoutFrame](../lwtk/LayoutFrame.md#inheritance) / [Control](../lwtk/Control.md#inheritance) / [HotkeyListener](../lwtk/HotkeyListener.md#inheritance) / **[Button](../lwtk/Button.md#inheritance)** / _**`TextLabel`**_
+
+## Constructor
+ * **`TextLabel(initParams)`**
+
+ * Overrides: **[Widget()](../lwtk/Widget.md#constructor)**
+ * Overrides: [Animatable()](../lwtk/Animatable.md#constructor)
+ * Overrides: [Styleable()](../lwtk/Styleable.md#constructor)
+ * Overrides: **[Component()](../lwtk/Component.md#constructor)**
+ * Overrides: [Actionable()](../lwtk/Actionable.md#constructor)
+
+
+
+## Methods
+ * **`TextLabel:getMeasures()`**
+
+ * Implements: [Component:getMeasures()](../lwtk/Component.md#.getMeasures)
+
+
+ * **`TextLabel:onHotkeyDisabled(hotkey)`**
+
+ * Overrides: [HotkeyListener:onHotkeyDisabled()](../lwtk/HotkeyListener.md#.onHotkeyDisabled)
+
+
+ * **`TextLabel:onHotkeyDown()`**
+
+
+ * **`TextLabel:onHotkeyEnabled(hotkey)`**
+
+ * Overrides: [HotkeyListener:onHotkeyEnabled()](../lwtk/HotkeyListener.md#.onHotkeyEnabled)
+ * Implements: [Component:onHotkeyEnabled()](../lwtk/Component.md#.onHotkeyEnabled)
+
+
+ * **`TextLabel:onLayout(width, height)`**
+
+ * Overrides: [LayoutFrame:onLayout()](../lwtk/LayoutFrame.md#.onLayout)
+ * Implements: [Component:onLayout()](../lwtk/Component.md#.onLayout)
+
+
+ * **`TextLabel:onMouseDown(x, y, button, modState)`**
+
+ * Implements: [Node:onMouseDown()](../lwtk/Node.md#.onMouseDown)
+
+
+ * **`TextLabel:setInput(inputId)`**
+
+
+ * **`TextLabel:setText(text)`**
+
+
+
+## Inherited Methods
+ * [HotkeyListener](../lwtk/HotkeyListener.md):
+ * [isHotkeyEnabled()](../lwtk/HotkeyListener.md#.isHotkeyEnabled), [onDisabled()](../lwtk/HotkeyListener.md#.onDisabled), [onEffectiveVisibilityChanged()](../lwtk/HotkeyListener.md#.onEffectiveVisibilityChanged), [setHotkey()](../lwtk/HotkeyListener.md#.setHotkey), [_handleHasFocusHandler()](../lwtk/HotkeyListener.md#._handleHasFocusHandler)
+ * [LayoutFrame](../lwtk/LayoutFrame.md):
+ * [addChild()](../lwtk/LayoutFrame.md#.addChild), [onDraw()](../lwtk/LayoutFrame.md#.onDraw)
+ * [Compound](../lwtk/Compound.md):
+ * [_processChanges()](../lwtk/Compound.md#._processChanges), [_processDraw()](../lwtk/Compound.md#._processDraw)
+ * **[Widget](../lwtk/Widget.md)**:
+ * [notifyInputChanged()](../lwtk/Widget.md#.notifyInputChanged), [setOnInputChanged()](../lwtk/Widget.md#.setOnInputChanged), [setOnRealize()](../lwtk/Widget.md#.setOnRealize), [_setApp()](../lwtk/Widget.md#._setApp), [_setParent()](../lwtk/Widget.md#._setParent)
+ * [Animatable](../lwtk/Animatable.md):
+ * [animateFrame()](../lwtk/Animatable.md#.animateFrame), [getStyle()](../lwtk/Animatable.md#.getStyle), [getStyleParam()](../lwtk/Animatable.md#.getStyleParam), [isVisible()](../lwtk/Animatable.md#.isVisible), [setState()](../lwtk/Animatable.md#.setState), [setStates()](../lwtk/Animatable.md#.setStates), [setStyle()](../lwtk/Animatable.md#.setStyle), [setVisible()](../lwtk/Animatable.md#.setVisible), [updateAnimation()](../lwtk/Animatable.md#.updateAnimation), [_setStyleFromParent()](../lwtk/Animatable.md#._setStyleFromParent)
+ * [Styleable](../lwtk/Styleable.md):
+ * [clearStyleCache()](../lwtk/Styleable.md#.clearStyleCache), [getStateString()](../lwtk/Styleable.md#.getStateString), [_getStyleParam()](../lwtk/Styleable.md#._getStyleParam)
+ * **[Component](../lwtk/Component.md)**:
+ * [byId()](../lwtk/Component.md#.byId), [getCurrentTime()](../lwtk/Component.md#.getCurrentTime), [getFocusHandler()](../lwtk/Component.md#.getFocusHandler), [getFontInfo()](../lwtk/Component.md#.getFontInfo), [getFrame()](../lwtk/Component.md#.getFrame), [getLayoutContext()](../lwtk/Component.md#.getLayoutContext), [getParent()](../lwtk/Component.md#.getParent), [getRoot()](../lwtk/Component.md#.getRoot), [getSize()](../lwtk/Component.md#.getSize), [handleRemainingInitParams()](../lwtk/Component.md#.handleRemainingInitParams), [parentById()](../lwtk/Component.md#.parentById), [setFrame()](../lwtk/Component.md#.setFrame), [setInitParams()](../lwtk/Component.md#.setInitParams), [setTimer()](../lwtk/Component.md#.setTimer), [transformXY()](../lwtk/Component.md#.transformXY), [triggerLayout()](../lwtk/Component.md#.triggerLayout), [triggerRedraw()](../lwtk/Component.md#.triggerRedraw), [updateFrameTransition()](../lwtk/Component.md#.updateFrameTransition), [_setFrame()](../lwtk/Component.md#._setFrame)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam), [_processMouseDown()](../lwtk/Drawable.md#._processMouseDown), [_processMouseEnter()](../lwtk/Drawable.md#._processMouseEnter), [_processMouseLeave()](../lwtk/Drawable.md#._processMouseLeave), [_processMouseMove()](../lwtk/Drawable.md#._processMouseMove), [_processMouseScroll()](../lwtk/Drawable.md#._processMouseScroll), [_processMouseUp()](../lwtk/Drawable.md#._processMouseUp)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / [Actionable](../lwtk/Actionable.md#subclasses) / [Node](../lwtk/Node.md#subclasses) / [Drawable](../lwtk/Drawable.md#subclasses) / **[Component](../lwtk/Component.md#subclasses)** / [Styleable](../lwtk/Styleable.md#subclasses) / [Animatable](../lwtk/Animatable.md#subclasses) / **[Widget](../lwtk/Widget.md#subclasses)** / [Compound](../lwtk/Compound.md#subclasses) / [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) / [HotkeyListener](../lwtk/HotkeyListener.md#subclasses) / **[Button](../lwtk/Button.md#subclasses)** / _**`TextLabel`**_ / **[TitleText](../lwtk/TitleText.md#inheritance)**
+
diff --git a/doc/gen/lwtk/Timer.md b/doc/gen/lwtk/Timer.md
new file mode 100644
index 0000000..a28e2eb
--- /dev/null
+++ b/doc/gen/lwtk/Timer.md
@@ -0,0 +1,21 @@
+# Class lwtk.Timer
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / _**`Timer`**_
+
+## Constructor
+ * **`Timer(func, ...)`**
+
+
+
+## Inherited Methods
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/TitleText.md b/doc/gen/lwtk/TitleText.md
new file mode 100644
index 0000000..45bc98a
--- /dev/null
+++ b/doc/gen/lwtk/TitleText.md
@@ -0,0 +1,42 @@
+# Class lwtk.TitleText
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) / [LayoutFrame](../lwtk/LayoutFrame.md#inheritance) / [Control](../lwtk/Control.md#inheritance) / [HotkeyListener](../lwtk/HotkeyListener.md#inheritance) / **[Button](../lwtk/Button.md#inheritance)** / **[TextLabel](../lwtk/TextLabel.md#inheritance)** / _**`TitleText`**_
+
+## Constructor
+ * **`TitleText(initParams)`**
+
+ * Inherited from: **[TextLabel()](../lwtk/TextLabel.md#constructor)**
+
+
+## Inherited Methods
+ * **[TextLabel](../lwtk/TextLabel.md)**:
+ * [getMeasures()](../lwtk/TextLabel.md#.getMeasures), [onHotkeyDisabled()](../lwtk/TextLabel.md#.onHotkeyDisabled), [onHotkeyDown()](../lwtk/TextLabel.md#.onHotkeyDown), [onHotkeyEnabled()](../lwtk/TextLabel.md#.onHotkeyEnabled), [onLayout()](../lwtk/TextLabel.md#.onLayout), [onMouseDown()](../lwtk/TextLabel.md#.onMouseDown), [setInput()](../lwtk/TextLabel.md#.setInput), [setText()](../lwtk/TextLabel.md#.setText)
+ * [HotkeyListener](../lwtk/HotkeyListener.md):
+ * [isHotkeyEnabled()](../lwtk/HotkeyListener.md#.isHotkeyEnabled), [onDisabled()](../lwtk/HotkeyListener.md#.onDisabled), [onEffectiveVisibilityChanged()](../lwtk/HotkeyListener.md#.onEffectiveVisibilityChanged), [setHotkey()](../lwtk/HotkeyListener.md#.setHotkey), [_handleHasFocusHandler()](../lwtk/HotkeyListener.md#._handleHasFocusHandler)
+ * [LayoutFrame](../lwtk/LayoutFrame.md):
+ * [addChild()](../lwtk/LayoutFrame.md#.addChild), [onDraw()](../lwtk/LayoutFrame.md#.onDraw)
+ * [Compound](../lwtk/Compound.md):
+ * [_processChanges()](../lwtk/Compound.md#._processChanges), [_processDraw()](../lwtk/Compound.md#._processDraw)
+ * **[Widget](../lwtk/Widget.md)**:
+ * [notifyInputChanged()](../lwtk/Widget.md#.notifyInputChanged), [setOnInputChanged()](../lwtk/Widget.md#.setOnInputChanged), [setOnRealize()](../lwtk/Widget.md#.setOnRealize), [_setApp()](../lwtk/Widget.md#._setApp), [_setParent()](../lwtk/Widget.md#._setParent)
+ * [Animatable](../lwtk/Animatable.md):
+ * [animateFrame()](../lwtk/Animatable.md#.animateFrame), [getStyle()](../lwtk/Animatable.md#.getStyle), [getStyleParam()](../lwtk/Animatable.md#.getStyleParam), [isVisible()](../lwtk/Animatable.md#.isVisible), [setState()](../lwtk/Animatable.md#.setState), [setStates()](../lwtk/Animatable.md#.setStates), [setStyle()](../lwtk/Animatable.md#.setStyle), [setVisible()](../lwtk/Animatable.md#.setVisible), [updateAnimation()](../lwtk/Animatable.md#.updateAnimation), [_setStyleFromParent()](../lwtk/Animatable.md#._setStyleFromParent)
+ * [Styleable](../lwtk/Styleable.md):
+ * [clearStyleCache()](../lwtk/Styleable.md#.clearStyleCache), [getStateString()](../lwtk/Styleable.md#.getStateString), [_getStyleParam()](../lwtk/Styleable.md#._getStyleParam)
+ * **[Component](../lwtk/Component.md)**:
+ * [byId()](../lwtk/Component.md#.byId), [getCurrentTime()](../lwtk/Component.md#.getCurrentTime), [getFocusHandler()](../lwtk/Component.md#.getFocusHandler), [getFontInfo()](../lwtk/Component.md#.getFontInfo), [getFrame()](../lwtk/Component.md#.getFrame), [getLayoutContext()](../lwtk/Component.md#.getLayoutContext), [getParent()](../lwtk/Component.md#.getParent), [getRoot()](../lwtk/Component.md#.getRoot), [getSize()](../lwtk/Component.md#.getSize), [handleRemainingInitParams()](../lwtk/Component.md#.handleRemainingInitParams), [parentById()](../lwtk/Component.md#.parentById), [setFrame()](../lwtk/Component.md#.setFrame), [setInitParams()](../lwtk/Component.md#.setInitParams), [setTimer()](../lwtk/Component.md#.setTimer), [transformXY()](../lwtk/Component.md#.transformXY), [triggerLayout()](../lwtk/Component.md#.triggerLayout), [triggerRedraw()](../lwtk/Component.md#.triggerRedraw), [updateFrameTransition()](../lwtk/Component.md#.updateFrameTransition), [_setFrame()](../lwtk/Component.md#._setFrame)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam), [_processMouseDown()](../lwtk/Drawable.md#._processMouseDown), [_processMouseEnter()](../lwtk/Drawable.md#._processMouseEnter), [_processMouseLeave()](../lwtk/Drawable.md#._processMouseLeave), [_processMouseMove()](../lwtk/Drawable.md#._processMouseMove), [_processMouseScroll()](../lwtk/Drawable.md#._processMouseScroll), [_processMouseUp()](../lwtk/Drawable.md#._processMouseUp)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/ViewSwitcher.md b/doc/gen/lwtk/ViewSwitcher.md
new file mode 100644
index 0000000..17960e0
--- /dev/null
+++ b/doc/gen/lwtk/ViewSwitcher.md
@@ -0,0 +1,69 @@
+# Class lwtk.ViewSwitcher
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [addChild()](#.addChild)
+ * [getMeasures()](#.getMeasures)
+ * [onLayout()](#.onLayout)
+ * [showChild()](#.showChild)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / **[Widget](../lwtk/Widget.md#inheritance)** / [Compound](../lwtk/Compound.md#inheritance) / [MouseDispatcher](../lwtk/MouseDispatcher.md#inheritance) / **[Group](../lwtk/Group.md#inheritance)** / [Colored](../lwtk/Colored.md#inheritance) / _**`ViewSwitcher`**_
+
+## Constructor
+ * **`ViewSwitcher(initParams)`**
+
+ * Inherited from: **[Group()](../lwtk/Group.md#constructor)**
+
+
+## Methods
+ * **`ViewSwitcher:addChild(child)`**
+
+ * Overrides: [Group:addChild()](../lwtk/Group.md#.addChild)
+ * Overrides: [Compound:addChild()](../lwtk/Compound.md#.addChild)
+ * Implements: [Drawable:addChild()](../lwtk/Drawable.md#.addChild)
+
+
+ * **`ViewSwitcher:getMeasures()`**
+
+ * Implements: [Component:getMeasures()](../lwtk/Component.md#.getMeasures)
+
+
+ * **`ViewSwitcher:onLayout(w, h)`**
+
+ * Implements: [Component:onLayout()](../lwtk/Component.md#.onLayout)
+
+
+ * **`ViewSwitcher:showChild(child)`**
+
+
+
+## Inherited Methods
+ * [Colored](../lwtk/Colored.md):
+ * [onDraw()](../lwtk/Colored.md#.onDraw)
+ * **[Group](../lwtk/Group.md)**:
+ * [childById()](../lwtk/Group.md#.childById), [_clearChildLookup()](../lwtk/Group.md#._clearChildLookup)
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md):
+ * [_processMouseDown()](../lwtk/MouseDispatcher.md#._processMouseDown), [_processMouseEnter()](../lwtk/MouseDispatcher.md#._processMouseEnter), [_processMouseLeave()](../lwtk/MouseDispatcher.md#._processMouseLeave), [_processMouseMove()](../lwtk/MouseDispatcher.md#._processMouseMove), [_processMouseScroll()](../lwtk/MouseDispatcher.md#._processMouseScroll), [_processMouseUp()](../lwtk/MouseDispatcher.md#._processMouseUp)
+ * [Compound](../lwtk/Compound.md):
+ * [_processChanges()](../lwtk/Compound.md#._processChanges), [_processDraw()](../lwtk/Compound.md#._processDraw)
+ * **[Widget](../lwtk/Widget.md)**:
+ * [notifyInputChanged()](../lwtk/Widget.md#.notifyInputChanged), [setOnInputChanged()](../lwtk/Widget.md#.setOnInputChanged), [setOnRealize()](../lwtk/Widget.md#.setOnRealize), [_setApp()](../lwtk/Widget.md#._setApp), [_setParent()](../lwtk/Widget.md#._setParent)
+ * [Animatable](../lwtk/Animatable.md):
+ * [animateFrame()](../lwtk/Animatable.md#.animateFrame), [getStyle()](../lwtk/Animatable.md#.getStyle), [getStyleParam()](../lwtk/Animatable.md#.getStyleParam), [isVisible()](../lwtk/Animatable.md#.isVisible), [setState()](../lwtk/Animatable.md#.setState), [setStates()](../lwtk/Animatable.md#.setStates), [setStyle()](../lwtk/Animatable.md#.setStyle), [setVisible()](../lwtk/Animatable.md#.setVisible), [updateAnimation()](../lwtk/Animatable.md#.updateAnimation), [_setStyleFromParent()](../lwtk/Animatable.md#._setStyleFromParent)
+ * [Styleable](../lwtk/Styleable.md):
+ * [clearStyleCache()](../lwtk/Styleable.md#.clearStyleCache), [getStateString()](../lwtk/Styleable.md#.getStateString), [_getStyleParam()](../lwtk/Styleable.md#._getStyleParam)
+ * **[Component](../lwtk/Component.md)**:
+ * [byId()](../lwtk/Component.md#.byId), [getCurrentTime()](../lwtk/Component.md#.getCurrentTime), [getFocusHandler()](../lwtk/Component.md#.getFocusHandler), [getFontInfo()](../lwtk/Component.md#.getFontInfo), [getFrame()](../lwtk/Component.md#.getFrame), [getLayoutContext()](../lwtk/Component.md#.getLayoutContext), [getParent()](../lwtk/Component.md#.getParent), [getRoot()](../lwtk/Component.md#.getRoot), [getSize()](../lwtk/Component.md#.getSize), [handleRemainingInitParams()](../lwtk/Component.md#.handleRemainingInitParams), [parentById()](../lwtk/Component.md#.parentById), [setFrame()](../lwtk/Component.md#.setFrame), [setInitParams()](../lwtk/Component.md#.setInitParams), [setTimer()](../lwtk/Component.md#.setTimer), [transformXY()](../lwtk/Component.md#.transformXY), [triggerLayout()](../lwtk/Component.md#.triggerLayout), [triggerRedraw()](../lwtk/Component.md#.triggerRedraw), [updateFrameTransition()](../lwtk/Component.md#.updateFrameTransition), [_setFrame()](../lwtk/Component.md#._setFrame)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/WeakKeysTable.md b/doc/gen/lwtk/WeakKeysTable.md
new file mode 100644
index 0000000..8579c30
--- /dev/null
+++ b/doc/gen/lwtk/WeakKeysTable.md
@@ -0,0 +1,3 @@
+# Meta lwtk.WeakKeysTable
+
+
diff --git a/doc/gen/lwtk/Widget.md b/doc/gen/lwtk/Widget.md
new file mode 100644
index 0000000..bd284df
--- /dev/null
+++ b/doc/gen/lwtk/Widget.md
@@ -0,0 +1,81 @@
+# Class lwtk.Widget
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [notifyInputChanged()](#.notifyInputChanged)
+ * [setOnInputChanged()](#.setOnInputChanged)
+ * [setOnRealize()](#.setOnRealize)
+ * [_setApp()](#._setApp)
+ * [_setParent()](#._setParent)
+ * [Inherited Methods](#inherited-methods)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / **[Component](../lwtk/Component.md#inheritance)** / [Styleable](../lwtk/Styleable.md#inheritance) / [Animatable](../lwtk/Animatable.md#inheritance) / _**`Widget`**_
+
+## Constructor
+ * **`Widget(initParams)`**
+
+ * Overrides: [Animatable()](../lwtk/Animatable.md#constructor)
+ * Overrides: [Styleable()](../lwtk/Styleable.md#constructor)
+ * Overrides: **[Component()](../lwtk/Component.md#constructor)**
+ * Overrides: [Actionable()](../lwtk/Actionable.md#constructor)
+
+
+
+## Methods
+ * **`Widget:notifyInputChanged()`**
+
+
+ * **`Widget:setOnInputChanged(onInputChanged)`**
+
+
+ * **`Widget:setOnRealize(onRealize)`**
+
+
+ * **`Widget:_setApp(app)`**
+
+ * Overrides: [Component:_setApp()](../lwtk/Component.md#._setApp)
+
+
+ * **`Widget:_setParent(parent)`**
+
+ * Overrides: [Component:_setParent()](../lwtk/Component.md#._setParent)
+
+
+
+## Inherited Methods
+ * [Animatable](../lwtk/Animatable.md):
+ * [animateFrame()](../lwtk/Animatable.md#.animateFrame), [getStyle()](../lwtk/Animatable.md#.getStyle), [getStyleParam()](../lwtk/Animatable.md#.getStyleParam), [isVisible()](../lwtk/Animatable.md#.isVisible), [setState()](../lwtk/Animatable.md#.setState), [setStates()](../lwtk/Animatable.md#.setStates), [setStyle()](../lwtk/Animatable.md#.setStyle), [setVisible()](../lwtk/Animatable.md#.setVisible), [updateAnimation()](../lwtk/Animatable.md#.updateAnimation), [_setStyleFromParent()](../lwtk/Animatable.md#._setStyleFromParent)
+ * [Styleable](../lwtk/Styleable.md):
+ * [clearStyleCache()](../lwtk/Styleable.md#.clearStyleCache), [getStateString()](../lwtk/Styleable.md#.getStateString), [_getStyleParam()](../lwtk/Styleable.md#._getStyleParam)
+ * **[Component](../lwtk/Component.md)**:
+ * [byId()](../lwtk/Component.md#.byId), [getCurrentTime()](../lwtk/Component.md#.getCurrentTime), [getFocusHandler()](../lwtk/Component.md#.getFocusHandler), [getFontInfo()](../lwtk/Component.md#.getFontInfo), [getFrame()](../lwtk/Component.md#.getFrame), [getLayoutContext()](../lwtk/Component.md#.getLayoutContext), [getParent()](../lwtk/Component.md#.getParent), [getRoot()](../lwtk/Component.md#.getRoot), [getSize()](../lwtk/Component.md#.getSize), [handleRemainingInitParams()](../lwtk/Component.md#.handleRemainingInitParams), [parentById()](../lwtk/Component.md#.parentById), [setFrame()](../lwtk/Component.md#.setFrame), [setInitParams()](../lwtk/Component.md#.setInitParams), [setTimer()](../lwtk/Component.md#.setTimer), [transformXY()](../lwtk/Component.md#.transformXY), [triggerLayout()](../lwtk/Component.md#.triggerLayout), [triggerRedraw()](../lwtk/Component.md#.triggerRedraw), [updateFrameTransition()](../lwtk/Component.md#.updateFrameTransition), [_processChanges()](../lwtk/Component.md#._processChanges), [_processDraw()](../lwtk/Component.md#._processDraw), [_setFrame()](../lwtk/Component.md#._setFrame)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam), [_processMouseDown()](../lwtk/Drawable.md#._processMouseDown), [_processMouseEnter()](../lwtk/Drawable.md#._processMouseEnter), [_processMouseLeave()](../lwtk/Drawable.md#._processMouseLeave), [_processMouseMove()](../lwtk/Drawable.md#._processMouseMove), [_processMouseScroll()](../lwtk/Drawable.md#._processMouseScroll), [_processMouseUp()](../lwtk/Drawable.md#._processMouseUp)
+ * [Actionable](../lwtk/Actionable.md):
+ * [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
+
+## Subclasses
+ * / **[Object](../lwtk/Object.md#subclasses)** / [Actionable](../lwtk/Actionable.md#subclasses) / [Node](../lwtk/Node.md#subclasses) / [Drawable](../lwtk/Drawable.md#subclasses) / **[Component](../lwtk/Component.md#subclasses)** / [Styleable](../lwtk/Styleable.md#subclasses) / [Animatable](../lwtk/Animatable.md#subclasses) / _**`Widget`**_ / [Compound](../lwtk/Compound.md#subclasses) /
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[TextInput](../lwtk/TextInput.md#inheritance)**
+ * [HotkeyListener](../lwtk/HotkeyListener.md#subclasses) / **[Button](../lwtk/Button.md#subclasses)** /
+ * [Focusable](../lwtk/Focusable.md#subclasses) / **[PushButton](../lwtk/PushButton.md#inheritance)**
+ * **[TextLabel](../lwtk/TextLabel.md#subclasses)** / **[TitleText](../lwtk/TitleText.md#inheritance)**
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md#subclasses) / **[Group](../lwtk/Group.md#subclasses)** /
+ * [Colored](../lwtk/Colored.md#subclasses) / **[ViewSwitcher](../lwtk/ViewSwitcher.md#inheritance)**
+ * **[Column](../lwtk/Column.md#inheritance)**
+ * [LayoutFrame](../lwtk/LayoutFrame.md#subclasses) / [Control](../lwtk/Control.md#subclasses) / **[Box](../lwtk/Box.md#subclasses)** / [Focusable](../lwtk/Focusable.md#subclasses) / **[FocusGroup](../lwtk/FocusGroup.md#inheritance)**
+ * **[Matrix](../lwtk/Matrix.md#inheritance)**
+ * **[Row](../lwtk/Row.md#inheritance)**
+ * **[Space](../lwtk/Space.md#inheritance)**
+ * **[Square](../lwtk/Square.md#inheritance)**
+
diff --git a/doc/gen/lwtk/Window.md b/doc/gen/lwtk/Window.md
new file mode 100644
index 0000000..bb5e039
--- /dev/null
+++ b/doc/gen/lwtk/Window.md
@@ -0,0 +1,217 @@
+# Class lwtk.Window
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [addChild()](#.addChild)
+ * [byId()](#.byId)
+ * [childById()](#.childById)
+ * [close()](#.close)
+ * [getCurrentTime()](#.getCurrentTime)
+ * [getFontInfo()](#.getFontInfo)
+ * [getLayoutContext()](#.getLayoutContext)
+ * [getNativeHandle()](#.getNativeHandle)
+ * [getRoot()](#.getRoot)
+ * [getSize()](#.getSize)
+ * [hide()](#.hide)
+ * [isClosed()](#.isClosed)
+ * [requestClose()](#.requestClose)
+ * [requestFocus()](#.requestFocus)
+ * [setColor()](#.setColor)
+ * [setFrame()](#.setFrame)
+ * [setInterceptKeyDown()](#.setInterceptKeyDown)
+ * [setInterceptMouseDown()](#.setInterceptMouseDown)
+ * [setMaxSize()](#.setMaxSize)
+ * [setMaxSizeFixed()](#.setMaxSizeFixed)
+ * [setMinSize()](#.setMinSize)
+ * [setOnClose()](#.setOnClose)
+ * [setOnSizeRequest()](#.setOnSizeRequest)
+ * [setSize()](#.setSize)
+ * [setTimer()](#.setTimer)
+ * [setTitle()](#.setTitle)
+ * [show()](#.show)
+ * [triggerLayout()](#.triggerLayout)
+ * [triggerRedraw()](#.triggerRedraw)
+ * [_clearChildLookup()](#._clearChildLookup)
+ * [_handleConfigure()](#._handleConfigure)
+ * [_handleExpose()](#._handleExpose)
+ * [_handleFocusIn()](#._handleFocusIn)
+ * [_handleFocusOut()](#._handleFocusOut)
+ * [_handleMap()](#._handleMap)
+ * [_handleMouseDown()](#._handleMouseDown)
+ * [_handleMouseEnter()](#._handleMouseEnter)
+ * [_handleMouseLeave()](#._handleMouseLeave)
+ * [_handleMouseMove()](#._handleMouseMove)
+ * [_handleMouseScroll()](#._handleMouseScroll)
+ * [_handleMouseUp()](#._handleMouseUp)
+ * [_postProcessChanges()](#._postProcessChanges)
+ * [_processChanges()](#._processChanges)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../lwtk/Object.md#inheritance)** / [Actionable](../lwtk/Actionable.md#inheritance) / [Node](../lwtk/Node.md#inheritance) / [Drawable](../lwtk/Drawable.md#inheritance) / [Styleable](../lwtk/Styleable.md#inheritance) / [KeyHandler](../lwtk/KeyHandler.md#inheritance) / [MouseDispatcher](../lwtk/MouseDispatcher.md#inheritance) / _**`Window`**_
+
+## Constructor
+ * **`Window(app, initParams)`**
+
+ * Overrides: [MouseDispatcher()](../lwtk/MouseDispatcher.md#constructor)
+ * Overrides: [KeyHandler()](../lwtk/KeyHandler.md#constructor)
+ * Overrides: [Styleable()](../lwtk/Styleable.md#constructor)
+ * Overrides: [Actionable()](../lwtk/Actionable.md#constructor)
+
+
+
+## Methods
+ * **`Window:addChild(child)`**
+
+ * Implements: [Drawable:addChild()](../lwtk/Drawable.md#.addChild)
+
+
+ * **`Window:byId(id)`**
+
+
+ * **`Window:childById(id)`**
+
+ * Implementation: [Group:childById()](../lwtk/Group.md#childById)
+
+ * **`Window:close()`**
+
+
+ * **`Window:getCurrentTime()`**
+
+
+ * **`Window:getFontInfo(family, slant, weight, size)`**
+
+
+ * **`Window:getLayoutContext()`**
+
+
+ * **`Window:getNativeHandle()`**
+
+
+ * **`Window:getRoot()`**
+
+ * Implementation: [Component:getRoot()](../lwtk/Component.md#getRoot)
+
+ * **`Window:getSize()`**
+
+
+ * **`Window:hide()`**
+
+
+ * **`Window:isClosed()`**
+
+
+ * **`Window:requestClose()`**
+
+
+ * **`Window:requestFocus()`**
+
+
+ * **`Window:setColor(color)`**
+
+
+ * **`Window:setFrame(x, y, w, h)`**
+
+
+ * **`Window:setInterceptKeyDown(interceptKeyDown)`**
+
+
+ * **`Window:setInterceptMouseDown(interceptMouseDown)`**
+
+
+ * **`Window:setMaxSize(w, h)`**
+
+
+ * **`Window:setMaxSizeFixed(flag)`**
+
+
+ * **`Window:setMinSize(w, h)`**
+
+
+ * **`Window:setOnClose(onClose)`**
+
+
+ * **`Window:setOnSizeRequest(onSizeRequest)`**
+
+
+ * **`Window:setSize(w, h)`**
+
+
+ * **`Window:setTimer(seconds, func, ...)`**
+
+
+ * **`Window:setTitle(title)`**
+
+
+ * **`Window:show()`**
+
+
+ * **`Window:triggerLayout()`**
+
+ * Implementation: [Component:triggerLayout()](../lwtk/Component.md#triggerLayout)
+
+ * **`Window:triggerRedraw()`**
+
+ * Implementation: [Component:triggerRedraw()](../lwtk/Component.md#triggerRedraw)
+
+ * **`Window:_clearChildLookup()`**
+
+
+ * **`Window:_handleConfigure(x, y, w, h)`**
+
+
+ * **`Window:_handleExpose(x, y, w, h, count)`**
+
+
+ * **`Window:_handleFocusIn()`**
+
+
+ * **`Window:_handleFocusOut()`**
+
+
+ * **`Window:_handleMap()`**
+
+
+ * **`Window:_handleMouseDown(mx, my, button, modState)`**
+
+
+ * **`Window:_handleMouseEnter(mx, my)`**
+
+
+ * **`Window:_handleMouseLeave(mx, my)`**
+
+
+ * **`Window:_handleMouseMove(mx, my)`**
+
+
+ * **`Window:_handleMouseScroll(dx, dy)`**
+
+
+ * **`Window:_handleMouseUp(mx, my, button, modState)`**
+
+
+ * **`Window:_postProcessChanges()`**
+
+
+ * **`Window:_processChanges()`**
+
+
+
+## Inherited Methods
+ * [MouseDispatcher](../lwtk/MouseDispatcher.md):
+ * [_processMouseDown()](../lwtk/MouseDispatcher.md#._processMouseDown), [_processMouseEnter()](../lwtk/MouseDispatcher.md#._processMouseEnter), [_processMouseLeave()](../lwtk/MouseDispatcher.md#._processMouseLeave), [_processMouseMove()](../lwtk/MouseDispatcher.md#._processMouseMove), [_processMouseScroll()](../lwtk/MouseDispatcher.md#._processMouseScroll), [_processMouseUp()](../lwtk/MouseDispatcher.md#._processMouseUp)
+ * [KeyHandler](../lwtk/KeyHandler.md):
+ * [resetKeyHandling()](../lwtk/KeyHandler.md#.resetKeyHandling), [_handleKeyDown()](../lwtk/KeyHandler.md#._handleKeyDown), [_handleKeyUp()](../lwtk/KeyHandler.md#._handleKeyUp)
+ * [Styleable](../lwtk/Styleable.md):
+ * [clearStyleCache()](../lwtk/Styleable.md#.clearStyleCache), [getStateString()](../lwtk/Styleable.md#.getStateString), [getStyle()](../lwtk/Styleable.md#.getStyle), [getStyleParam()](../lwtk/Styleable.md#.getStyleParam), [setState()](../lwtk/Styleable.md#.setState), [setStyle()](../lwtk/Styleable.md#.setStyle), [_getStyleParam()](../lwtk/Styleable.md#._getStyleParam), [_setStyleFromParent()](../lwtk/Styleable.md#._setStyleFromParent)
+ * [Drawable](../lwtk/Drawable.md):
+ * [getMandatoryStyleParam()](../lwtk/Drawable.md#.getMandatoryStyleParam)
+ * [Actionable](../lwtk/Actionable.md):
+ * [handleRemainingInitParams()](../lwtk/Actionable.md#.handleRemainingInitParams), [hasActionMethod()](../lwtk/Actionable.md#.hasActionMethod), [invokeActionMethod()](../lwtk/Actionable.md#.invokeActionMethod), [setInitParams()](../lwtk/Actionable.md#.setInitParams)
+ * **[Object](../lwtk/Object.md)**:
+ * [getClass()](../lwtk/Object.md#.getClass), [getClassPath()](../lwtk/Object.md#.getClassPath), [getMember()](../lwtk/Object.md#.getMember), [getReverseClassPath()](../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../lwtk/Object.md#.isInstanceOf), [setAttributes()](../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/_VERSION.md b/doc/gen/lwtk/_VERSION.md
new file mode 100644
index 0000000..5b7f4f6
--- /dev/null
+++ b/doc/gen/lwtk/_VERSION.md
@@ -0,0 +1,3 @@
+# lwtk._VERSION
+
+
diff --git a/doc/gen/lwtk/btest.md b/doc/gen/lwtk/btest.md
new file mode 100644
index 0000000..6f5feb6
--- /dev/null
+++ b/doc/gen/lwtk/btest.md
@@ -0,0 +1,6 @@
+# Function lwtk.btest
+
+ * **`btest(...)`**
+
+
+ Returns *true* if bitwise AND of its operands is different from zero.
diff --git a/doc/gen/lwtk/call.md b/doc/gen/lwtk/call.md
new file mode 100644
index 0000000..6c0cea2
--- /dev/null
+++ b/doc/gen/lwtk/call.md
@@ -0,0 +1,5 @@
+# Function lwtk.call
+
+ * **`call(methodName, obj, ...)`**
+
+
diff --git a/doc/gen/lwtk/errorf.md b/doc/gen/lwtk/errorf.md
new file mode 100644
index 0000000..ad6d6a0
--- /dev/null
+++ b/doc/gen/lwtk/errorf.md
@@ -0,0 +1,5 @@
+# Function lwtk.errorf
+
+ * **`errorf(formatString, ...)`**
+
+
diff --git a/doc/gen/lwtk/extract.md b/doc/gen/lwtk/extract.md
new file mode 100644
index 0000000..3de8aff
--- /dev/null
+++ b/doc/gen/lwtk/extract.md
@@ -0,0 +1,5 @@
+# Function lwtk.extract
+
+ * **`extract(table, key)`**
+
+
diff --git a/doc/gen/lwtk/get.md b/doc/gen/lwtk/get.md
new file mode 100644
index 0000000..1a27524
--- /dev/null
+++ b/doc/gen/lwtk/get.md
@@ -0,0 +1,3 @@
+# Table lwtk.get
+
+
diff --git a/doc/gen/lwtk/getSuperClass.md b/doc/gen/lwtk/getSuperClass.md
new file mode 100644
index 0000000..fa0f5c4
--- /dev/null
+++ b/doc/gen/lwtk/getSuperClass.md
@@ -0,0 +1,5 @@
+# Function lwtk.getSuperClass
+
+ * **`getSuperClass(class)`**
+
+
diff --git a/doc/gen/lwtk/init.md b/doc/gen/lwtk/init.md
new file mode 100644
index 0000000..d5ba6cf
--- /dev/null
+++ b/doc/gen/lwtk/init.md
@@ -0,0 +1,4 @@
+# Table lwtk
+
+Root module for all other *lwtk* modules.
+
diff --git a/doc/gen/lwtk/isInstanceOf.md b/doc/gen/lwtk/isInstanceOf.md
new file mode 100644
index 0000000..2cc6436
--- /dev/null
+++ b/doc/gen/lwtk/isInstanceOf.md
@@ -0,0 +1,11 @@
+# Function lwtk.isInstanceOf
+
+ * **`isInstanceOf(self, C)`**
+
+ Determines if object is instance of the given class.
+
+ Returns *true*, if *self* is an object that was created by invoking class *C*
+ or by invoking a subclass of *C*.
+
+ Returns also *true* if *C* is a metatable of *self* or somewhere in the
+ metatable chain of *self*.
diff --git a/doc/gen/lwtk/layout.md b/doc/gen/lwtk/layout.md
new file mode 100644
index 0000000..8631596
--- /dev/null
+++ b/doc/gen/lwtk/layout.md
@@ -0,0 +1,3 @@
+# Table lwtk.layout
+
+
diff --git a/doc/gen/lwtk/love/Application.md b/doc/gen/lwtk/love/Application.md
new file mode 100644
index 0000000..97fdbeb
--- /dev/null
+++ b/doc/gen/lwtk/love/Application.md
@@ -0,0 +1,80 @@
+# Class lwtk.love.Application
+
+Application implementation for the [LÖVE](https://love2d.org/) 2D game engine.
+
+Use [lwtk.Application](../../lwtk/Application.md) for runing standalone desktop applications.
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [draw()](#.draw)
+ * [focus()](#.focus)
+ * [keypressed()](#.keypressed)
+ * [keyreleased()](#.keyreleased)
+ * [mousefocus()](#.mousefocus)
+ * [mousemoved()](#.mousemoved)
+ * [mousepressed()](#.mousepressed)
+ * [mousereleased()](#.mousereleased)
+ * [setFocusWindow()](#.setFocusWindow)
+ * [textinput()](#.textinput)
+ * [update()](#.update)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../../lwtk/Object.md#inheritance)** / **[Application](../../lwtk/Application.md#inheritance)** / [Node](../../lwtk/Node.md#inheritance) / [MouseDispatcher](../../lwtk/MouseDispatcher.md#inheritance) / _**`love.Application`**_
+
+## Constructor
+ * **`love.Application(initParams)`**
+
+ * Overrides: [MouseDispatcher()](../../lwtk/MouseDispatcher.md#constructor)
+ * Overrides: **[Application()](../../lwtk/Application.md#constructor)**
+
+
+
+## Methods
+ * **`love.Application:draw()`**
+
+
+ * **`love.Application:focus(focus)`**
+
+
+ * **`love.Application:keypressed(key)`**
+
+
+ * **`love.Application:keyreleased(key)`**
+
+
+ * **`love.Application:mousefocus(focus)`**
+
+
+ * **`love.Application:mousemoved(x, y)`**
+
+
+ * **`love.Application:mousepressed(x, y, button)`**
+
+
+ * **`love.Application:mousereleased(x, y, button)`**
+
+
+ * **`love.Application:setFocusWindow(window)`**
+
+
+ * **`love.Application:textinput(text)`**
+
+
+ * **`love.Application:update()`**
+
+ * Overrides: [Application:update()](../../lwtk/Application.md#.update)
+
+
+
+## Inherited Methods
+ * [MouseDispatcher](../../lwtk/MouseDispatcher.md):
+ * [_processMouseDown()](../../lwtk/MouseDispatcher.md#._processMouseDown), [_processMouseEnter()](../../lwtk/MouseDispatcher.md#._processMouseEnter), [_processMouseLeave()](../../lwtk/MouseDispatcher.md#._processMouseLeave), [_processMouseMove()](../../lwtk/MouseDispatcher.md#._processMouseMove), [_processMouseScroll()](../../lwtk/MouseDispatcher.md#._processMouseScroll), [_processMouseUp()](../../lwtk/MouseDispatcher.md#._processMouseUp)
+ * **[Application](../../lwtk/Application.md)**:
+ * [addStyle()](../../lwtk/Application.md#.addStyle), [close()](../../lwtk/Application.md#.close), [deferChanges()](../../lwtk/Application.md#.deferChanges), [getCurrentTime()](../../lwtk/Application.md#.getCurrentTime), [getFontInfo()](../../lwtk/Application.md#.getFontInfo), [getLayoutContext()](../../lwtk/Application.md#.getLayoutContext), [getScreenScale()](../../lwtk/Application.md#.getScreenScale), [hasWindows()](../../lwtk/Application.md#.hasWindows), [newWindow()](../../lwtk/Application.md#.newWindow), [runEventLoop()](../../lwtk/Application.md#.runEventLoop), [setErrorFunc()](../../lwtk/Application.md#.setErrorFunc), [setExtensions()](../../lwtk/Application.md#.setExtensions), [setStyle()](../../lwtk/Application.md#.setStyle), [setTimer()](../../lwtk/Application.md#.setTimer), [_addWindow()](../../lwtk/Application.md#._addWindow), [_processAllChanges()](../../lwtk/Application.md#._processAllChanges), [_removeWindow()](../../lwtk/Application.md#._removeWindow)
+ * **[Object](../../lwtk/Object.md)**:
+ * [getClass()](../../lwtk/Object.md#.getClass), [getClassPath()](../../lwtk/Object.md#.getClassPath), [getMember()](../../lwtk/Object.md#.getMember), [getReverseClassPath()](../../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../../lwtk/Object.md#.isInstanceOf), [setAttributes()](../../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/love/DrawContext.md b/doc/gen/lwtk/love/DrawContext.md
new file mode 100644
index 0000000..93ad923
--- /dev/null
+++ b/doc/gen/lwtk/love/DrawContext.md
@@ -0,0 +1,82 @@
+# Class lwtk.love.DrawContext
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [beginOpacity()](#.beginOpacity)
+ * [drawBorder()](#.drawBorder)
+ * [drawLine()](#.drawLine)
+ * [drawText()](#.drawText)
+ * [endOpacity()](#.endOpacity)
+ * [fillRect()](#.fillRect)
+ * [intersectClip()](#.intersectClip)
+ * [restore()](#.restore)
+ * [save()](#.save)
+ * [setColor()](#.setColor)
+ * [setLineWidth()](#.setLineWidth)
+ * [translate()](#.translate)
+ * [_reset()](#._reset)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../../lwtk/Object.md#inheritance)** / **[love.LayoutContext](../../lwtk/love/LayoutContext.md#inheritance)** / _**`love.DrawContext`**_
+
+## Constructor
+ * **`love.DrawContext(...)`**
+
+ * Overrides: **[love.LayoutContext()](../../lwtk/love/LayoutContext.md#constructor)**
+
+
+
+## Methods
+ * **`love.DrawContext:beginOpacity(opacity)`**
+
+
+ * **`love.DrawContext:drawBorder(color, borderThickness, x, y, w, h)`**
+
+
+ * **`love.DrawContext:drawLine(x1, y1, x2, y2, ...)`**
+
+
+ * **`love.DrawContext:drawText(x, y, text)`**
+
+
+ * **`love.DrawContext:endOpacity()`**
+
+
+ * **`love.DrawContext:fillRect(color, x, y, w, h)`**
+
+
+ * **`love.DrawContext:intersectClip(x, y, w, h)`**
+
+
+ * **`love.DrawContext:restore()`**
+
+
+ * **`love.DrawContext:save()`**
+
+
+ * **`love.DrawContext:setColor(r, g, b, a)`**
+
+
+ * **`love.DrawContext:setLineWidth(w)`**
+
+
+ * **`love.DrawContext:translate(x, y)`**
+
+
+ * **`love.DrawContext:_reset()`**
+
+ * Overrides: [love.LayoutContext:_reset()](../../lwtk/love/LayoutContext.md#._reset)
+
+
+
+## Inherited Methods
+ * **[love.LayoutContext](../../lwtk/love/LayoutContext.md)**:
+ * [getFontHeightMeasures()](../../lwtk/love/LayoutContext.md#.getFontHeightMeasures), [getTextMeasures()](../../lwtk/love/LayoutContext.md#.getTextMeasures), [getTextWidth()](../../lwtk/love/LayoutContext.md#.getTextWidth), [selectFont()](../../lwtk/love/LayoutContext.md#.selectFont)
+ * **[Object](../../lwtk/Object.md)**:
+ * [getClass()](../../lwtk/Object.md#.getClass), [getClassPath()](../../lwtk/Object.md#.getClassPath), [getMember()](../../lwtk/Object.md#.getMember), [getReverseClassPath()](../../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../../lwtk/Object.md#.isInstanceOf), [setAttributes()](../../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/love/Driver.md b/doc/gen/lwtk/love/Driver.md
new file mode 100644
index 0000000..f279d6e
--- /dev/null
+++ b/doc/gen/lwtk/love/Driver.md
@@ -0,0 +1,68 @@
+# Class lwtk.love.Driver
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [close()](#.close)
+ * [getDrawContext()](#.getDrawContext)
+ * [getLayoutContext()](#.getLayoutContext)
+ * [getScreenScale()](#.getScreenScale)
+ * [getTime()](#.getTime)
+ * [grabFocus()](#.grabFocus)
+ * [newView()](#.newView)
+ * [setErrorFunc()](#.setErrorFunc)
+ * [setMinSize()](#.setMinSize)
+ * [setNextProcessTime()](#.setNextProcessTime)
+ * [setProcessFunc()](#.setProcessFunc)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../../lwtk/Object.md#inheritance)** / _**`love.Driver`**_
+
+## Constructor
+ * **`love.Driver(initParams)`**
+
+
+
+## Methods
+ * **`love.Driver:close()`**
+
+
+ * **`love.Driver:getDrawContext()`**
+
+
+ * **`love.Driver:getLayoutContext()`**
+
+
+ * **`love.Driver:getScreenScale()`**
+
+
+ * **`love.Driver:getTime()`**
+
+
+ * **`love.Driver:grabFocus(window)`**
+
+
+ * **`love.Driver:newView(window, initParams)`**
+
+
+ * **`love.Driver:setErrorFunc(...)`**
+
+
+ * **`love.Driver:setMinSize(window, minW, minH)`**
+
+
+ * **`love.Driver:setNextProcessTime(t)`**
+
+
+ * **`love.Driver:setProcessFunc(f)`**
+
+
+
+## Inherited Methods
+ * **[Object](../../lwtk/Object.md)**:
+ * [getClass()](../../lwtk/Object.md#.getClass), [getClassPath()](../../lwtk/Object.md#.getClassPath), [getMember()](../../lwtk/Object.md#.getMember), [getReverseClassPath()](../../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../../lwtk/Object.md#.isInstanceOf), [setAttributes()](../../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/love/LayoutContext.md b/doc/gen/lwtk/love/LayoutContext.md
new file mode 100644
index 0000000..ca4961e
--- /dev/null
+++ b/doc/gen/lwtk/love/LayoutContext.md
@@ -0,0 +1,49 @@
+# Class lwtk.love.LayoutContext
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [getFontHeightMeasures()](#.getFontHeightMeasures)
+ * [getTextMeasures()](#.getTextMeasures)
+ * [getTextWidth()](#.getTextWidth)
+ * [selectFont()](#.selectFont)
+ * [_reset()](#._reset)
+ * [Inherited Methods](#inherited-methods)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../../lwtk/Object.md#inheritance)** / _**`love.LayoutContext`**_
+
+## Constructor
+ * **`love.LayoutContext()`**
+
+
+
+## Methods
+ * **`love.LayoutContext:getFontHeightMeasures()`**
+
+
+ * **`love.LayoutContext:getTextMeasures(text)`**
+
+
+ * **`love.LayoutContext:getTextWidth(text)`**
+
+
+ * **`love.LayoutContext:selectFont(family, slant, weight, size)`**
+
+
+ * **`love.LayoutContext:_reset()`**
+
+
+
+## Inherited Methods
+ * **[Object](../../lwtk/Object.md)**:
+ * [getClass()](../../lwtk/Object.md#.getClass), [getClassPath()](../../lwtk/Object.md#.getClassPath), [getMember()](../../lwtk/Object.md#.getMember), [getReverseClassPath()](../../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../../lwtk/Object.md#.isInstanceOf), [setAttributes()](../../lwtk/Object.md#.setAttributes)
+
+## Subclasses
+ * / **[Object](../../lwtk/Object.md#subclasses)** / _**`love.LayoutContext`**_ / **[love.DrawContext](../../lwtk/love/DrawContext.md#inheritance)**
+
diff --git a/doc/gen/lwtk/love/View.md b/doc/gen/lwtk/love/View.md
new file mode 100644
index 0000000..910a817
--- /dev/null
+++ b/doc/gen/lwtk/love/View.md
@@ -0,0 +1,56 @@
+# Class lwtk.love.View
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [close()](#.close)
+ * [isClosed()](#.isClosed)
+ * [postRedisplay()](#.postRedisplay)
+ * [setFrame()](#.setFrame)
+ * [setMaxSize()](#.setMaxSize)
+ * [setMinSize()](#.setMinSize)
+ * [setSize()](#.setSize)
+ * [show()](#.show)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../../lwtk/Object.md#inheritance)** / _**`love.View`**_
+
+## Constructor
+ * **`love.View(window, initParams)`**
+
+
+
+## Methods
+ * **`love.View:close()`**
+
+
+ * **`love.View:isClosed()`**
+
+
+ * **`love.View:postRedisplay(x, y, w, h)`**
+
+
+ * **`love.View:setFrame(x, y, w, h)`**
+
+
+ * **`love.View:setMaxSize()`**
+
+
+ * **`love.View:setMinSize()`**
+
+
+ * **`love.View:setSize(w, h)`**
+
+
+ * **`love.View:show()`**
+
+
+
+## Inherited Methods
+ * **[Object](../../lwtk/Object.md)**:
+ * [getClass()](../../lwtk/Object.md#.getClass), [getClassPath()](../../lwtk/Object.md#.getClassPath), [getMember()](../../lwtk/Object.md#.getMember), [getReverseClassPath()](../../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../../lwtk/Object.md#.isInstanceOf), [setAttributes()](../../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/love/init.md b/doc/gen/lwtk/love/init.md
new file mode 100644
index 0000000..8006028
--- /dev/null
+++ b/doc/gen/lwtk/love/init.md
@@ -0,0 +1,4 @@
+# Table lwtk.love
+
+Modules for using *lwtk* with the [LÖVE](https://love2d.org/) 2D game engine.
+
diff --git a/doc/gen/lwtk/love/keyNameMap.md b/doc/gen/lwtk/love/keyNameMap.md
new file mode 100644
index 0000000..e96d994
--- /dev/null
+++ b/doc/gen/lwtk/love/keyNameMap.md
@@ -0,0 +1,3 @@
+# Table lwtk.love.keyNameMap
+
+
diff --git a/doc/gen/lwtk/lpugl/CairoDrawContext.md b/doc/gen/lwtk/lpugl/CairoDrawContext.md
new file mode 100644
index 0000000..daa28b0
--- /dev/null
+++ b/doc/gen/lwtk/lpugl/CairoDrawContext.md
@@ -0,0 +1,80 @@
+# Class lwtk.lpugl.CairoDrawContext
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [beginOpacity()](#.beginOpacity)
+ * [drawBorder()](#.drawBorder)
+ * [drawLine()](#.drawLine)
+ * [drawText()](#.drawText)
+ * [endOpacity()](#.endOpacity)
+ * [fillRect()](#.fillRect)
+ * [intersectClip()](#.intersectClip)
+ * [restore()](#.restore)
+ * [save()](#.save)
+ * [setColor()](#.setColor)
+ * [setLineWidth()](#.setLineWidth)
+ * [translate()](#.translate)
+ * [_setCairoContext()](#._setCairoContext)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../../lwtk/Object.md#inheritance)** / **[lpugl.CairoLayoutContext](../../lwtk/lpugl/CairoLayoutContext.md#inheritance)** / _**`lpugl.CairoDrawContext`**_
+
+## Constructor
+ * **`lpugl.CairoDrawContext(...)`**
+
+ * Overrides: **[lpugl.CairoLayoutContext()](../../lwtk/lpugl/CairoLayoutContext.md#constructor)**
+
+
+
+## Methods
+ * **`lpugl.CairoDrawContext:beginOpacity(opacity)`**
+
+
+ * **`lpugl.CairoDrawContext:drawBorder(color, borderThickness, x, y, w, h)`**
+
+
+ * **`lpugl.CairoDrawContext:drawLine(x1, y1, x2, y2, ...)`**
+
+
+ * **`lpugl.CairoDrawContext:drawText(x, y, text)`**
+
+
+ * **`lpugl.CairoDrawContext:endOpacity()`**
+
+
+ * **`lpugl.CairoDrawContext:fillRect(color, x, y, w, h)`**
+
+
+ * **`lpugl.CairoDrawContext:intersectClip(x, y, w, h)`**
+
+
+ * **`lpugl.CairoDrawContext:restore()`**
+
+
+ * **`lpugl.CairoDrawContext:save()`**
+
+
+ * **`lpugl.CairoDrawContext:setColor(r, g, b, a)`**
+
+
+ * **`lpugl.CairoDrawContext:setLineWidth(w)`**
+
+
+ * **`lpugl.CairoDrawContext:translate(x, y)`**
+
+
+ * **`lpugl.CairoDrawContext:_setCairoContext(cairoCtx)`**
+
+
+
+## Inherited Methods
+ * **[lpugl.CairoLayoutContext](../../lwtk/lpugl/CairoLayoutContext.md)**:
+ * [getFontHeightMeasures()](../../lwtk/lpugl/CairoLayoutContext.md#.getFontHeightMeasures), [getTextMeasures()](../../lwtk/lpugl/CairoLayoutContext.md#.getTextMeasures), [getTextWidth()](../../lwtk/lpugl/CairoLayoutContext.md#.getTextWidth), [selectFont()](../../lwtk/lpugl/CairoLayoutContext.md#.selectFont)
+ * **[Object](../../lwtk/Object.md)**:
+ * [getClass()](../../lwtk/Object.md#.getClass), [getClassPath()](../../lwtk/Object.md#.getClassPath), [getMember()](../../lwtk/Object.md#.getMember), [getReverseClassPath()](../../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../../lwtk/Object.md#.isInstanceOf), [setAttributes()](../../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/lpugl/CairoLayoutContext.md b/doc/gen/lwtk/lpugl/CairoLayoutContext.md
new file mode 100644
index 0000000..a2da7c9
--- /dev/null
+++ b/doc/gen/lwtk/lpugl/CairoLayoutContext.md
@@ -0,0 +1,45 @@
+# Class lwtk.lpugl.CairoLayoutContext
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [getFontHeightMeasures()](#.getFontHeightMeasures)
+ * [getTextMeasures()](#.getTextMeasures)
+ * [getTextWidth()](#.getTextWidth)
+ * [selectFont()](#.selectFont)
+ * [Inherited Methods](#inherited-methods)
+ * [Subclasses](#subclasses)
+
+
+## Inheritance
+ * / **[Object](../../lwtk/Object.md#inheritance)** / _**`lpugl.CairoLayoutContext`**_
+
+## Constructor
+ * **`lpugl.CairoLayoutContext(cairoCtx, platform)`**
+
+
+
+## Methods
+ * **`lpugl.CairoLayoutContext:getFontHeightMeasures()`**
+
+
+ * **`lpugl.CairoLayoutContext:getTextMeasures(text)`**
+
+
+ * **`lpugl.CairoLayoutContext:getTextWidth(text)`**
+
+
+ * **`lpugl.CairoLayoutContext:selectFont(family, slant, weight, size)`**
+
+
+
+## Inherited Methods
+ * **[Object](../../lwtk/Object.md)**:
+ * [getClass()](../../lwtk/Object.md#.getClass), [getClassPath()](../../lwtk/Object.md#.getClassPath), [getMember()](../../lwtk/Object.md#.getMember), [getReverseClassPath()](../../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../../lwtk/Object.md#.isInstanceOf), [setAttributes()](../../lwtk/Object.md#.setAttributes)
+
+## Subclasses
+ * / **[Object](../../lwtk/Object.md#subclasses)** / _**`lpugl.CairoLayoutContext`**_ / **[lpugl.CairoDrawContext](../../lwtk/lpugl/CairoDrawContext.md#inheritance)**
+
diff --git a/doc/gen/lwtk/lpugl/Driver.md b/doc/gen/lwtk/lpugl/Driver.md
new file mode 100644
index 0000000..0e2bc22
--- /dev/null
+++ b/doc/gen/lwtk/lpugl/Driver.md
@@ -0,0 +1,72 @@
+# Class lwtk.lpugl.Driver
+
+
+## Contents
+
+ * [Inheritance](#inheritance)
+ * [Constructor](#constructor)
+ * [Methods](#methods)
+ * [close()](#.close)
+ * [getDrawContext()](#.getDrawContext)
+ * [getLayoutContext()](#.getLayoutContext)
+ * [getScreenScale()](#.getScreenScale)
+ * [getTime()](#.getTime)
+ * [grabFocus()](#.grabFocus)
+ * [handleNextEvents()](#.handleNextEvents)
+ * [hasViews()](#.hasViews)
+ * [newView()](#.newView)
+ * [setErrorFunc()](#.setErrorFunc)
+ * [setNextProcessTime()](#.setNextProcessTime)
+ * [setProcessFunc()](#.setProcessFunc)
+ * [Inherited Methods](#inherited-methods)
+
+
+## Inheritance
+ * / **[Object](../../lwtk/Object.md#inheritance)** / _**`lpugl.Driver`**_
+
+## Constructor
+ * **`lpugl.Driver(initParams)`**
+
+
+
+## Methods
+ * **`lpugl.Driver:close()`**
+
+
+ * **`lpugl.Driver:getDrawContext(view)`**
+
+
+ * **`lpugl.Driver:getLayoutContext()`**
+
+
+ * **`lpugl.Driver:getScreenScale()`**
+
+
+ * **`lpugl.Driver:getTime()`**
+
+
+ * **`lpugl.Driver:grabFocus(window)`**
+
+
+ * **`lpugl.Driver:handleNextEvents(timeout)`**
+
+
+ * **`lpugl.Driver:hasViews()`**
+
+
+ * **`lpugl.Driver:newView(window, initParams)`**
+
+
+ * **`lpugl.Driver:setErrorFunc(...)`**
+
+
+ * **`lpugl.Driver:setNextProcessTime(t)`**
+
+
+ * **`lpugl.Driver:setProcessFunc(f)`**
+
+
+
+## Inherited Methods
+ * **[Object](../../lwtk/Object.md)**:
+ * [getClass()](../../lwtk/Object.md#.getClass), [getClassPath()](../../lwtk/Object.md#.getClassPath), [getMember()](../../lwtk/Object.md#.getMember), [getReverseClassPath()](../../lwtk/Object.md#.getReverseClassPath), [getSuperClass()](../../lwtk/Object.md#.getSuperClass), [isInstanceOf()](../../lwtk/Object.md#.isInstanceOf), [setAttributes()](../../lwtk/Object.md#.setAttributes)
diff --git a/doc/gen/lwtk/lpugl/init.md b/doc/gen/lwtk/lpugl/init.md
new file mode 100644
index 0000000..6d628d7
--- /dev/null
+++ b/doc/gen/lwtk/lpugl/init.md
@@ -0,0 +1,5 @@
+# Table lwtk.lpugl
+
+Modules for using *lwtk* on top of [LPugl](https://github.com/osch/lua-lpugl#lpugl),
+a minimal Lua-API for building GUIs for Linux, Windows or macOS.
+
diff --git a/doc/gen/lwtk/newClass.md b/doc/gen/lwtk/newClass.md
new file mode 100644
index 0000000..929cd80
--- /dev/null
+++ b/doc/gen/lwtk/newClass.md
@@ -0,0 +1,15 @@
+# Function lwtk.newClass
+
+ * **`newClass(className, superClass, ...)`**
+
+ Creates new class object. A class object has [lwtk.Class](../lwtk/Class.md) as metatable
+ and [lwtk.type](../lwtk/type.md)() evaluates to `"lwtk.Class"`.
+
+ * *className* - string value
+ * *superClass* - optional class object, if not given, [lwtk.Object](../lwtk/Object.md)
+ is taken as superclass.
+ * *...* - optional further arguments that are reached over
+ to [lwtk.Object.newSubClass()](../lwtk/Object.md#.newSubClass).
+
+ See [lwtk.Class Usage](../../Class.md) for detailed documentation
+ and usage examples.
diff --git a/doc/gen/lwtk/newMeta.md b/doc/gen/lwtk/newMeta.md
new file mode 100644
index 0000000..f13f24c
--- /dev/null
+++ b/doc/gen/lwtk/newMeta.md
@@ -0,0 +1,9 @@
+# Function lwtk.newMeta
+
+ * **`newMeta(name)`**
+
+ Creates new meta object. A meta object has [lwtk.Meta](../lwtk/Meta.md) as metatable
+ and [lwtk.type](../lwtk/type.md)() evaluates to `"lwtk.Meta"`.
+
+ See [lwtk.Meta Usage](../../Meta.md) for detailed documentation
+ and usage examples.
diff --git a/doc/gen/lwtk/newMixin.md b/doc/gen/lwtk/newMixin.md
new file mode 100644
index 0000000..d7026aa
--- /dev/null
+++ b/doc/gen/lwtk/newMixin.md
@@ -0,0 +1,9 @@
+# Function lwtk.newMixin
+
+ * **`newMixin(name, ...)`**
+
+ Creates new mixin object. A mixin object has [lwtk.Mixin](../lwtk/Mixin.md) as metatable
+ and [lwtk.type](../lwtk/type.md)() evaluates to `"lwtk.Mixin"`.
+
+ See [lwtk.Mixin Usage](../../Mixin.md) for detailed documentation
+ and usage examples.
diff --git a/doc/gen/lwtk/tryrequire.md b/doc/gen/lwtk/tryrequire.md
new file mode 100644
index 0000000..1a86021
--- /dev/null
+++ b/doc/gen/lwtk/tryrequire.md
@@ -0,0 +1,5 @@
+# Function lwtk.tryrequire
+
+ * **`tryrequire(name)`**
+
+
diff --git a/doc/gen/lwtk/type.md b/doc/gen/lwtk/type.md
new file mode 100644
index 0000000..0cfbefc
--- /dev/null
+++ b/doc/gen/lwtk/type.md
@@ -0,0 +1,15 @@
+# Function lwtk.type
+
+ * **`type(arg)`**
+
+ Returns the type name.
+
+ * If *arg* is of type *"table"* or *"userdata"* and has a metatable of
+ type *"table"* with a field *"__name"*, than the value of this field
+ is returned.
+
+ * If *arg* is of type *"table"* or *"userdata"* and has a metatable of
+ type *"string"*, than this string value is returned.
+
+ * In all other cases this functions returns the same value, as the
+ builtin Lua function *type()* would return.
diff --git a/doc/gen/lwtk/undef.md b/doc/gen/lwtk/undef.md
new file mode 100644
index 0000000..9835312
--- /dev/null
+++ b/doc/gen/lwtk/undef.md
@@ -0,0 +1,5 @@
+# Function lwtk.undef
+
+ * **`undef(class)`**
+
+
diff --git a/doc/gen/lwtk/utf8.md b/doc/gen/lwtk/utf8.md
new file mode 100644
index 0000000..d4a273e
--- /dev/null
+++ b/doc/gen/lwtk/utf8.md
@@ -0,0 +1,3 @@
+# Table lwtk.utf8
+
+
diff --git a/doc/gen/modules.md b/doc/gen/modules.md
new file mode 100644
index 0000000..7264163
--- /dev/null
+++ b/doc/gen/modules.md
@@ -0,0 +1,103 @@
+# Module Index
+
+ * [lwtk](#lwtk) - Root module for all other *lwtk* modules.
+ * [lwtk.love](#lwtklove) - Modules for using *lwtk* with the [LÖVE](https://love2d.org/) 2D game engine.
+ * [lwtk.lpugl](#lwtklpugl) - Modules for using *lwtk* on top of [LPugl](https://github.com/osch/lua-lpugl#lpugl), a minimal Lua-API for building GUIs for Linux, Windows or macOS.
+
+## lwtk
+
+### Classes
+ * **[lwtk.Animations](lwtk/Animations.md)**
+ * **[lwtk.Application](lwtk/Application.md)** - Default application implementation.
+ * **[lwtk.Area](lwtk/Area.md)** - A list of rectangle coordinates forming an area.
+ * **[lwtk.Box](lwtk/Box.md)**
+ * **[lwtk.Button](lwtk/Button.md)**
+ * **[lwtk.Color](lwtk/Color.md)** - RGBA Color value.
+ * **[lwtk.Column](lwtk/Column.md)**
+ * **[lwtk.Component](lwtk/Component.md)**
+ * **[lwtk.DefaultStyle](lwtk/DefaultStyle.md)**
+ * **[lwtk.FocusGroup](lwtk/FocusGroup.md)**
+ * **[lwtk.FocusHandler](lwtk/FocusHandler.md)**
+ * **[lwtk.FontInfo](lwtk/FontInfo.md)**
+ * **[lwtk.FontInfos](lwtk/FontInfos.md)**
+ * **[lwtk.Group](lwtk/Group.md)**
+ * **[lwtk.InnerCompound](lwtk/InnerCompound.md)**
+ * **[lwtk.Matrix](lwtk/Matrix.md)**
+ * **[lwtk.Object](lwtk/Object.md)** - Superclass for all classes created by [lwtk.newClass](lwtk/newClass.md)().
+ * **[lwtk.PushButton](lwtk/PushButton.md)**
+ * **[lwtk.Rect](lwtk/Rect.md)**
+ * **[lwtk.Row](lwtk/Row.md)**
+ * **[lwtk.Space](lwtk/Space.md)**
+ * **[lwtk.Square](lwtk/Square.md)**
+ * **[lwtk.Style](lwtk/Style.md)**
+ * **[lwtk.TextCursor](lwtk/TextCursor.md)**
+ * **[lwtk.TextFragment](lwtk/TextFragment.md)**
+ * **[lwtk.TextInput](lwtk/TextInput.md)**
+ * **[lwtk.TextLabel](lwtk/TextLabel.md)**
+ * **[lwtk.Timer](lwtk/Timer.md)**
+ * **[lwtk.TitleText](lwtk/TitleText.md)**
+ * **[lwtk.ViewSwitcher](lwtk/ViewSwitcher.md)**
+ * **[lwtk.Widget](lwtk/Widget.md)**
+ * **[lwtk.Window](lwtk/Window.md)**
+### Mixins
+ * [lwtk.Actionable](lwtk/Actionable.md)
+ * [lwtk.Animatable](lwtk/Animatable.md)
+ * [lwtk.Colored](lwtk/Colored.md)
+ * [lwtk.Compound](lwtk/Compound.md)
+ * [lwtk.Control](lwtk/Control.md)
+ * [lwtk.Drawable](lwtk/Drawable.md)
+ * [lwtk.Focusable](lwtk/Focusable.md)
+ * [lwtk.HotkeyListener](lwtk/HotkeyListener.md)
+ * [lwtk.KeyHandler](lwtk/KeyHandler.md)
+ * [lwtk.LayoutFrame](lwtk/LayoutFrame.md)
+ * [lwtk.MouseDispatcher](lwtk/MouseDispatcher.md)
+ * [lwtk.Node](lwtk/Node.md)
+ * [lwtk.Styleable](lwtk/Styleable.md)
+### Metas
+ * [lwtk.Callback](lwtk/Callback.md) - Holds a function with arguments that can be called.
+ * [lwtk.ChildLookup](lwtk/ChildLookup.md)
+ * [lwtk.KeyBinding](lwtk/KeyBinding.md)
+ * [lwtk.WeakKeysTable](lwtk/WeakKeysTable.md)
+### Functions
+ * [lwtk.BuiltinStyleTypes](lwtk/BuiltinStyleTypes.md)
+ * [lwtk.DefaultKeyBinding](lwtk/DefaultKeyBinding.md) - Returns new [lwtk.KeyBinding](lwtk/KeyBinding.md) object with default settings.
+ * [lwtk.btest](lwtk/btest.md) - Returns *true* if bitwise AND of its operands is different from zero.
+ * [lwtk.call](lwtk/call.md)
+ * [lwtk.errorf](lwtk/errorf.md)
+ * [lwtk.extract](lwtk/extract.md)
+ * [lwtk.getSuperClass](lwtk/getSuperClass.md)
+ * [lwtk.isInstanceOf](lwtk/isInstanceOf.md) - Determines if object is instance of the given class.
+ * [lwtk.newClass](lwtk/newClass.md) - Creates new class object.
+ * [lwtk.newMeta](lwtk/newMeta.md) - Creates new meta object.
+ * [lwtk.newMixin](lwtk/newMixin.md) - Creates new mixin object.
+ * [lwtk.tryrequire](lwtk/tryrequire.md)
+ * [lwtk.type](lwtk/type.md) - Returns the type name.
+ * [lwtk.undef](lwtk/undef.md)
+### Other
+ * [lwtk.Class](lwtk/Class.md) - Metatable for objects created by [lwtk.newClass](lwtk/newClass.md)().
+ * [lwtk.Meta](lwtk/Meta.md) - Metatable for objects created by [lwtk.newMeta](lwtk/newMeta.md)().
+ * [lwtk.Mixin](lwtk/Mixin.md) - Metatable for objects created by [lwtk.newMixin](lwtk/newMixin.md)().
+ * [lwtk.StyleRef](lwtk/StyleRef.md)
+ * [lwtk.StyleTypeAttributes](lwtk/StyleTypeAttributes.md)
+ * [lwtk._VERSION](lwtk/_VERSION.md)
+ * [lwtk.get](lwtk/get.md)
+ * [lwtk.layout](lwtk/layout.md)
+ * [lwtk.utf8](lwtk/utf8.md)
+
+## lwtk.love
+
+### Classes
+ * **[lwtk.love.Application](lwtk/love/Application.md)** - Application implementation for the [LÖVE](https://love2d.org/) 2D game engine.
+ * **[lwtk.love.DrawContext](lwtk/love/DrawContext.md)**
+ * **[lwtk.love.Driver](lwtk/love/Driver.md)**
+ * **[lwtk.love.LayoutContext](lwtk/love/LayoutContext.md)**
+ * **[lwtk.love.View](lwtk/love/View.md)**
+### Other
+ * [lwtk.love.keyNameMap](lwtk/love/keyNameMap.md)
+
+## lwtk.lpugl
+
+### Classes
+ * **[lwtk.lpugl.CairoDrawContext](lwtk/lpugl/CairoDrawContext.md)**
+ * **[lwtk.lpugl.CairoLayoutContext](lwtk/lpugl/CairoLayoutContext.md)**
+ * **[lwtk.lpugl.Driver](lwtk/lpugl/Driver.md)**
diff --git a/example/example01.lua b/example/example01.lua
index eabf67d..f3d73a8 100644
--- a/example/example01.lua
+++ b/example/example01.lua
@@ -18,12 +18,24 @@ local win = app:newWindow {
title = "example01",
Column {
id = "c1",
+ onInputChanged = function(widget, input)
+ widget:byId("b1"):setDisabled(input.text == "")
+ end,
+
TitleText { text = "What's your name?" },
+
TextInput { id = "i1", focus = true, style = { Columns = 40 } },
+
Row {
Space {},
PushButton { id = "b1", text = "&OK", disabled = true,
- default = true },
+ default = true,
+ onClicked = function(widget)
+ local name = widget:byId("i1").text
+ widget:byId("t2"):setText("Hello "..name.."!")
+ widget:byId("c1"):setVisible(false)
+ widget:byId("c2"):setVisible(true)
+ end },
PushButton { id = "b2", text = "&Quit", onClicked = quit },
Space {}
@@ -37,7 +49,14 @@ local win = app:newWindow {
Space {},
Row {
Space {},
- PushButton { id = "b3", text = "&Again" },
+ PushButton { id = "b3", text = "&Again",
+ onClicked = function(widget)
+ widget:byId("i1"):setText("")
+ widget:byId("i1"):setFocus()
+ widget:byId("t2"):setText("")
+ widget:byId("c2"):setVisible(false)
+ widget:byId("c1"):setVisible(true)
+ end },
PushButton { id = "b4", text = "&Quit", default = true,
onClicked = quit },
@@ -46,24 +65,6 @@ local win = app:newWindow {
}
}
-win:childById("c1"):setOnInputChanged(function(widget, input)
- widget:childById("b1"):setDisabled(input.text == "")
-end)
-
-win:childById("b1"):setOnClicked(function(widget)
- win:childById("t2"):setText("Hello "..win:childById("i1").text.."!")
- win:childById("c1"):setVisible(false)
- win:childById("c2"):setVisible(true)
-end)
-
-win:childById("b3"):setOnClicked(function(widget)
- win:childById("i1"):setText("")
- win:childById("i1"):setFocus()
- win:childById("t2"):setText("")
- win:childById("c1"):setVisible(true)
- win:childById("c2"):setVisible(false)
-end)
-
win:show()
app:runEventLoop()
diff --git a/example/example10.lua b/example/example10.lua
index 71abd1f..0df5273 100644
--- a/example/example10.lua
+++ b/example/example10.lua
@@ -8,6 +8,8 @@ local newClass = lwtk.newClass
local MyButton = newClass("MyButton", Widget)
do
+ MyButton:declare("onClicked", "text")
+
function MyButton:setOnClicked(onClicked)
self.onClicked = onClicked
end
@@ -15,22 +17,22 @@ do
self.text = text
self:triggerRedraw()
end
- function MyButton:onMouseEnter(x, y)
+ function MyButton.implement:onMouseEnter(x, y)
self:setState("hover", true)
end
- function MyButton:onMouseLeave(x, y)
+ function MyButton.implement:onMouseLeave(x, y)
self:setState("hover", false)
end
- function MyButton:onMouseDown(x, y, button, modState)
+ function MyButton.implement:onMouseDown(x, y, button, modState)
self:setState("pressed", true)
end
- function MyButton:onMouseUp(x, y, button, modState)
+ function MyButton.implement:onMouseUp(x, y, button, modState)
self:setState("pressed", false)
if self.state.hover and self.onClicked then
self:onClicked()
end
end
- function MyButton:onDraw(ctx)
+ function MyButton.implement:onDraw(ctx)
local w, h = self:getSize()
ctx:fillRect(self:getStyleParam("BackgroundColor"), 0, 0, w, h)
ctx:setColor(self:getStyleParam("TextColor"):toRGBA())
diff --git a/example/screenshot00.png b/example/screenshot00.png
new file mode 100644
index 0000000..db36e01
Binary files /dev/null and b/example/screenshot00.png differ
diff --git a/lwtk-scm-0.rockspec b/lwtk-scm-0.rockspec
index 54d2a7a..89fff67 100644
--- a/lwtk-scm-0.rockspec
+++ b/lwtk-scm-0.rockspec
@@ -19,15 +19,16 @@ dependencies = {
}
build = {
type = "builtin",
- modules = {
+ modules =
+ {
-- MODULES BEGIN
+ --
["lwtk"] = "src/lwtk/init.lua",
["lwtk.Actionable"] = "src/lwtk/Actionable.lua",
["lwtk.Animatable"] = "src/lwtk/Animatable.lua",
["lwtk.Animations"] = "src/lwtk/Animations.lua",
["lwtk.Application"] = "src/lwtk/Application.lua",
["lwtk.Area"] = "src/lwtk/Area.lua",
- ["lwtk.Bordered"] = "src/lwtk/Bordered.lua",
["lwtk.Box"] = "src/lwtk/Box.lua",
["lwtk.BuiltinStyleTypes"] = "src/lwtk/BuiltinStyleTypes.lua",
["lwtk.Button"] = "src/lwtk/Button.lua",
@@ -42,21 +43,23 @@ build = {
["lwtk.Control"] = "src/lwtk/Control.lua",
["lwtk.DefaultKeyBinding"] = "src/lwtk/DefaultKeyBinding.lua",
["lwtk.DefaultStyle"] = "src/lwtk/DefaultStyle.lua",
+ ["lwtk.Drawable"] = "src/lwtk/Drawable.lua",
["lwtk.FocusGroup"] = "src/lwtk/FocusGroup.lua",
["lwtk.FocusHandler"] = "src/lwtk/FocusHandler.lua",
["lwtk.Focusable"] = "src/lwtk/Focusable.lua",
["lwtk.FontInfo"] = "src/lwtk/FontInfo.lua",
["lwtk.FontInfos"] = "src/lwtk/FontInfos.lua",
["lwtk.Group"] = "src/lwtk/Group.lua",
- ["lwtk.HotkeyHandler"] = "src/lwtk/HotkeyHandler.lua",
["lwtk.HotkeyListener"] = "src/lwtk/HotkeyListener.lua",
["lwtk.InnerCompound"] = "src/lwtk/InnerCompound.lua",
["lwtk.KeyBinding"] = "src/lwtk/KeyBinding.lua",
["lwtk.KeyHandler"] = "src/lwtk/KeyHandler.lua",
["lwtk.LayoutFrame"] = "src/lwtk/LayoutFrame.lua",
["lwtk.Matrix"] = "src/lwtk/Matrix.lua",
+ ["lwtk.Meta"] = "src/lwtk/Meta.lua",
["lwtk.Mixin"] = "src/lwtk/Mixin.lua",
["lwtk.MouseDispatcher"] = "src/lwtk/MouseDispatcher.lua",
+ ["lwtk.Node"] = "src/lwtk/Node.lua",
["lwtk.Object"] = "src/lwtk/Object.lua",
["lwtk.PushButton"] = "src/lwtk/PushButton.lua",
["lwtk.Rect"] = "src/lwtk/Rect.lua",
@@ -76,7 +79,6 @@ build = {
["lwtk.ViewSwitcher"] = "src/lwtk/ViewSwitcher.lua",
["lwtk.WeakKeysTable"] = "src/lwtk/WeakKeysTable.lua",
["lwtk.Widget"] = "src/lwtk/Widget.lua",
- ["lwtk.WidgetWrapper"] = "src/lwtk/WidgetWrapper.lua",
["lwtk.Window"] = "src/lwtk/Window.lua",
["lwtk._VERSION"] = "src/lwtk/_VERSION.lua",
["lwtk.btest"] = "src/lwtk/btest.lua",
@@ -85,13 +87,6 @@ build = {
["lwtk.extract"] = "src/lwtk/extract.lua",
["lwtk.get"] = "src/lwtk/get.lua",
["lwtk.getSuperClass"] = "src/lwtk/getSuperClass.lua",
- ["lwtk.internal"] = "src/lwtk/internal/init.lua",
- ["lwtk.internal.ColumnImpl"] = "src/lwtk/internal/ColumnImpl.lua",
- ["lwtk.internal.LayoutImpl"] = "src/lwtk/internal/LayoutImpl.lua",
- ["lwtk.internal.StyleRule"] = "src/lwtk/internal/StyleRule.lua",
- ["lwtk.internal.StyleRuleContext"] = "src/lwtk/internal/StyleRuleContext.lua",
- ["lwtk.internal.TypeRule"] = "src/lwtk/internal/TypeRule.lua",
- ["lwtk.internal.utf8string"] = "src/lwtk/internal/utf8string.lua",
["lwtk.isInstanceOf"] = "src/lwtk/isInstanceOf.lua",
["lwtk.layout"] = "src/lwtk/layout.lua",
["lwtk.lpugl"] = "src/lwtk/lpugl/init.lua",
@@ -99,11 +94,22 @@ build = {
["lwtk.lpugl.CairoLayoutContext"] = "src/lwtk/lpugl/CairoLayoutContext.lua",
["lwtk.lpugl.Driver"] = "src/lwtk/lpugl/Driver.lua",
["lwtk.newClass"] = "src/lwtk/newClass.lua",
+ ["lwtk.newMeta"] = "src/lwtk/newMeta.lua",
["lwtk.newMixin"] = "src/lwtk/newMixin.lua",
["lwtk.tryrequire"] = "src/lwtk/tryrequire.lua",
["lwtk.type"] = "src/lwtk/type.lua",
+ ["lwtk.undef"] = "src/lwtk/undef.lua",
["lwtk.utf8"] = "src/lwtk/utf8.lua",
- ["lwtk.vivid"] = "src/lwtk/vivid.lua",
+ --
+ ["lwtk.internal"] = "src/lwtk/internal/init.lua",
+ ["lwtk.internal.ColumnImpl"] = "src/lwtk/internal/ColumnImpl.lua",
+ ["lwtk.internal.LayoutImpl"] = "src/lwtk/internal/LayoutImpl.lua",
+ ["lwtk.internal.StyleRule"] = "src/lwtk/internal/StyleRule.lua",
+ ["lwtk.internal.StyleRuleContext"] = "src/lwtk/internal/StyleRuleContext.lua",
+ ["lwtk.internal.TypeRule"] = "src/lwtk/internal/TypeRule.lua",
+ ["lwtk.internal.utf8string"] = "src/lwtk/internal/utf8string.lua",
+ ["lwtk.internal.vivid"] = "src/lwtk/internal/vivid.lua",
+ --
-- MODULES END
},
copy_directories = {}
diff --git a/scripts/substmodules.lua b/scripts/substmodules.lua
index a37e790..5c385f4 100644
--- a/scripts/substmodules.lua
+++ b/scripts/substmodules.lua
@@ -1,3 +1,4 @@
+os.setlocale("C")
local rockspecFilename, listFilename = ...
local inList = false
local rockspecFile = io.open(rockspecFilename, "r")
@@ -8,9 +9,10 @@ end
rockspecFile:close()
local out = {}
for _, line in ipairs(lines) do
- local m = line:match("^%s*%-%- MODULES ([A-Z_]+)")
+ local pre, m = line:match("^(%s*%-%-) MODULES ([A-Z_]+)")
if m == "BEGIN" then
out[#out+1] = line
+ out[#out+1] = pre
inList = true
local modules = {}
local files = {}
@@ -26,15 +28,28 @@ for _, line in ipairs(lines) do
files[module] = file
end
end
- table.sort(modules)
+ table.sort(modules, function(a,b)
+ local am, bm = a:match("^lwtk.internal"), b:match("^lwtk.internal")
+ if (am and bm) or (not am and not bm) then
+ return a < b
+ else
+ return not am
+ end
+ end)
+ local isInternal
for _, module in ipairs(modules) do
local file = files[module]
+ if not isInternal and module:match("^lwtk.internal") then
+ isInternal = true
+ out[#out+1] = pre
+ end
out[#out+1] = string.format([[ ["%s"]%s = "src/lwtk/%s",]],
module:gsub("%/", "."),
string.rep(" ", maxl - #module),
file)
end
elseif m == "END" then
+ out[#out+1] = pre
out[#out+1] = line
inList = false
elseif not inList then
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..8ef6d0d
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,11 @@
+.PHONY: default doc test doctest
+
+default: doc doctest
+
+doc:
+ lua mkdoc/main.lua
+
+test: doctest
+
+doctest:
+ lua doctest.lua $(sort $(wildcard ../doc/*.md))
diff --git a/src/alltests.lua b/src/alltests.lua
index 104dd79..5037938 100755
--- a/src/alltests.lua
+++ b/src/alltests.lua
@@ -1,29 +1,48 @@
#!/usr/bin/lua
-local search = (package.searchers or package.loaders)[2]
+local path = require("path") -- https://luarocks.org/modules/xavier-wang/lpath
+local fs = require("path.fs") -- https://luarocks.org/modules/xavier-wang/lpath
+
+os.setlocale("C")
+
+local args = {...}
+
+if #args == 1 or #args == 0 then
+ local dirName
+ if #args == 0 then
+ dirName = "./tests"
+ else
+ dirName = args[1]
+ end
+ for n, t in fs.scandir(dirName) do
+ if n:match("/test.*%.lua$") and t == "file" and path.isfile(n) then
+ args[#args + 1] = n
+ end
+ end
+ table.sort(args)
+end
local counter = 0
local tryNext = true
local tests = {}
-while tryNext do
+for argI = 1, #args do
+ local testFileName = args[argI]
counter = counter + 1
- local testname = string.format("test%02d", counter)
- local test = search("tests."..testname)
+ local testname = testFileName:gsub("(test.*)%.lua$", "%1")
+ local test = loadfile(testFileName)
if type(test) == "function" then
tests[#tests + 1] = testname
print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
print("@@@@@@@@@@@", testname)
print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
test()
- else
- print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
- for _, t in ipairs(tests) do
- print(t..": OK")
- end
- print("OK.")
- tryNext = false
end
end
+print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
+for _, t in ipairs(tests) do
+ print(t..": OK")
+end
+print("OK.")
\ No newline at end of file
diff --git a/src/doctest.lua b/src/doctest.lua
index 3f47557..8e5e67d 100755
--- a/src/doctest.lua
+++ b/src/doctest.lua
@@ -1,7 +1,23 @@
#!/usr/bin/lua
+os.setlocale("C")
+
local args = {...}
+if #args == 0 then
+ local path = require("path") -- https://luarocks.org/modules/xavier-wang/lpath
+ local fs = require("path.fs") -- https://luarocks.org/modules/xavier-wang/lpath
+
+ for n, t in fs.scandir("../doc") do
+ if n:match("%.md$") and not n:match("/gen/") and t == "file" and path.isfile(n) then
+ args[#args + 1] = n
+ end
+ end
+ table.sort(args)
+end
+
+local lwtk = require("lwtk")
+
function assertEq(a1, a2)
if not (a1 == a2) then
error("assertEq failed: "..tostring(a1).." <> "..tostring(a2), 2)
@@ -62,5 +78,5 @@ for argI = 1, #args do
error(err)
end
- scriptFunc()
+ scriptFunc()
end
diff --git a/src/lwtk/Actionable.lua b/src/lwtk/Actionable.lua
index b97ed3f..9999d2c 100644
--- a/src/lwtk/Actionable.lua
+++ b/src/lwtk/Actionable.lua
@@ -3,14 +3,21 @@ local lwtk = require"lwtk"
local match = string.match
local getActions = lwtk.get.actions
-local Super = lwtk.Object
-local Actionable = lwtk.newClass("lwtk.Actionable", Super)
+local Actionable = lwtk.newMixin("lwtk.Actionable",
-function Actionable:new(initParams)
- if initParams then
- self:setInitParams(initParams)
+ function(Actionable, Super)
+
+ function Actionable:new(initParams)
+ if initParams then
+ self:setInitParams(initParams)
+ end
+ end
+
+ function Actionable:handleRemainingInitParams(initParams)
+ Super.setAttributes(self, initParams)
+ end
end
-end
+)
function Actionable:setInitParams(initParams)
if initParams then
@@ -18,7 +25,9 @@ function Actionable:setInitParams(initParams)
local hasRemaining = false
for k, v in pairs(initParams) do
if type(k) == "string" and match(k, "^onAction") then
- if not objectActions then objectActions = {} end
+ if not objectActions then
+ objectActions = {}
+ end
objectActions[k] = v
initParams[k] = nil
else
@@ -29,12 +38,7 @@ function Actionable:setInitParams(initParams)
getActions[self] = objectActions
end
if hasRemaining then
- local handleRemainingInitParams = self.handleRemainingInitParams
- if handleRemainingInitParams then
- handleRemainingInitParams(self, initParams)
- else
- Super.setAttributes(self, initParams)
- end
+ self:handleRemainingInitParams(initParams)
end
end
end
@@ -49,7 +53,7 @@ function Actionable:hasActionMethod(actionMethodName)
end
function Actionable:invokeActionMethod(actionMethodName)
- local method = self[actionMethodName]
+ local method = self:getMember(actionMethodName)
if method then
return method(self)
else
diff --git a/src/lwtk/Animatable.lua b/src/lwtk/Animatable.lua
index 8e9e173..2651eb6 100644
--- a/src/lwtk/Animatable.lua
+++ b/src/lwtk/Animatable.lua
@@ -6,26 +6,27 @@ local getVisibilityChanges = lwtk.get.visibilityChanges
local callOnLayout = lwtk.layout.callOnLayout
-local Styleable = lwtk.Styleable
-local Animatable = lwtk.newMixin("lwtk.Animatable", Styleable, Styleable.NO_STYLE_SELECTOR)
-
local getParamTransitions = lwtk.WeakKeysTable()
local getStateTransitions = lwtk.WeakKeysTable()
local getCurrentValues = lwtk.WeakKeysTable()
-function Animatable.initClass(Animatable, Super) -- luacheck: ignore 431/Animatable
+local Styleable = lwtk.Styleable
+local Animatable = lwtk.newMixin("lwtk.Animatable", Styleable, Styleable.NO_STYLE_SELECTOR,
+
+ function(Animatable, Super)
+
+ Animatable:declare(
+ "_animationUpdated"
+ )
- function Animatable:new(initParams)
- getParamTransitions[self] = {}
- getStateTransitions[self] = {}
- getCurrentValues[self] = {}
-
- self._animationActive = false
- self._animationTriggered = false
- self._animationUpdated = false
- Super.new(self, initParams)
+ function Animatable.override:new(initParams)
+ getParamTransitions[self] = {}
+ getStateTransitions[self] = {}
+ getCurrentValues[self] = {}
+ Super.new(self, initParams)
+ end
end
-end
+)
local getStyleParam = Styleable.getStyleParam
@@ -34,7 +35,7 @@ local function addToAnimations(self)
app._animations:add(self)
end
-function Animatable:animateFrame(newX, newY, newW, newH, isLayoutTransition)
+function Animatable.override:animateFrame(newX, newY, newW, newH, isLayoutTransition)
if self.x ~= newX or self.y ~= newY
or self.w ~= newW or self.h ~= newH
then
@@ -242,7 +243,7 @@ function Animatable:setVisible(shouldBeVisible)
end
end
-function Animatable:setState(name, flag)
+function Animatable.override:setState(name, flag)
_setState(self, name, flag)
end
@@ -252,29 +253,29 @@ function Animatable:setStates(stateNames)
end
end
-function Animatable:_setStyleFromParent(parentStyle)
+function Animatable.override:_setStyleFromParent(parentStyle)
getCurrentValues[self] = {}
Styleable._setStyleFromParent(self, parentStyle)
end
-function Animatable:setStyle(style)
+function Animatable.override:setStyle(style)
getCurrentValues[self] = {}
Styleable.setStyle(self, style)
end
local function clearCaches(self)
getCurrentValues[self] = {}
- for _, c in ipairs(self) do
- clearCaches(c)
+ for i = 1, #self do
+ clearCaches(self[i])
end
end
-function Animatable:getStyle()
+function Animatable.override:getStyle()
clearCaches(self)
return Styleable.getStyle(self)
end
-function Animatable:getStyleParam(paramName)
+function Animatable.override:getStyleParam(paramName)
local value = getCurrentValues[self][paramName]
if value == nil then
value = getStyleParam(self, paramName)
@@ -291,15 +292,7 @@ function Animatable:getStyleParam(paramName)
return value
end
-function Animatable:getMandatoryStyleParam(paramName)
- local p = self:getStyleParam(paramName)
- if not p then
- lwtk.errorf("Missing StyleParam %q", paramName)
- end
- return p
-end
-
-function Animatable:updateAnimation()
+function Animatable.override:updateAnimation()
if self._animationUpdated then
return
end
diff --git a/src/lwtk/Animations.lua b/src/lwtk/Animations.lua
index 0a8670f..0c6d04b 100644
--- a/src/lwtk/Animations.lua
+++ b/src/lwtk/Animations.lua
@@ -1,23 +1,28 @@
local lwtk = require("lwtk")
local remove = table.remove
+local rawset = rawset
local UPDATE_INTERVAL = 0.015 -- seconds
local processAnimations
local Animations = lwtk.newClass("lwtk.Animations")
+Animations:declare(
+ "app",
+ "timer"
+)
+
function Animations:new(app)
self.app = app
- self.setTimer = app.setTimer
self.timer = lwtk.Timer(processAnimations, self)
end
function Animations:add(animatable)
- self[#self + 1] = animatable
+ rawset(self, #self + 1, animatable)
local timer = self.timer
if not timer.time then
- self:setTimer(UPDATE_INTERVAL, timer)
+ self.app:setTimer(UPDATE_INTERVAL, timer)
end
end
@@ -48,7 +53,7 @@ processAnimations = function(self)
if #self > 0 then
local timer = self.timer
if not timer.time then
- self:setTimer(UPDATE_INTERVAL, timer)
+ self.app:setTimer(UPDATE_INTERVAL, timer)
end
end
end
diff --git a/src/lwtk/Application.lua b/src/lwtk/Application.lua
index e1d6c34..012adbd 100644
--- a/src/lwtk/Application.lua
+++ b/src/lwtk/Application.lua
@@ -1,9 +1,11 @@
local lwtk = require"lwtk"
+local insert = table.insert
+local remove = table.remove
+
local Timer = lwtk.Timer
local Window = lwtk.Window
local FontInfos = lwtk.FontInfos
-local Application = lwtk.newClass("lwtk.Application")
local extract = lwtk.extract
local getApp = lwtk.get.app
@@ -14,8 +16,38 @@ local getVisibilityChanges = lwtk.get.visibilityChanges
local getDeferredChanges = lwtk.get.deferredChanges
local isInstanceOf = lwtk.isInstanceOf
-local isClosed = setmetatable({}, { __mode = "k" })
-local createClosures
+local isClosed = lwtk.WeakKeysTable()
+
+--[[
+ Default application implementation.
+
+ Use this for standalone desktop applications. Use lwtk.love.Application for
+ running within the [LÖVE](https://love2d.org/) 2D game engine.
+]]
+local Application = lwtk.newClass("lwtk.Application")
+
+Application:declare(
+ "_animations",
+ "_eventFunc",
+ "_hasChanges",
+ "animationTimer",
+ "appName",
+ "driver",
+ "postprocessNeeded",
+ "procssingDeferedChanges",
+ "scale",
+ "timers"
+)
+
+local function unpack2(t, i, n)
+ if i <= n then
+ return t[i], unpack2(t, i + 1, n)
+ end
+end
+local function unpack(t)
+ return unpack2(t, 1, t.n)
+end
+
function Application:new(arg1, arg2)
local appName
@@ -31,13 +63,17 @@ function Application:new(arg1, arg2)
initParams = arg1 or {}
appName = extract(initParams, "name")
end
- self.driver = extract(initParams, "driver")
- if not self.driver then
+ local driver = extract(initParams, "driver")
+ if not driver then
assert(appName, "Application object needs name attribute")
- self.driver = lwtk.lpugl.Driver{ appName = appName }
+ driver = lwtk.lpugl.Driver{ appName = appName }
end
+ self.driver = driver
isClosed[self] = false
-
+
+ local timers = {}
+ self.timers = timers
+
local style = initParams.style
if style then
initParams.style = nil
@@ -55,7 +91,7 @@ function Application:new(arg1, arg2)
end
getVisibilityChanges[self] = lwtk.WeakKeysTable()
- getDeferredChanges[self] = lwtk.WeakKeysTable()
+ getDeferredChanges[self] = {}
if getApp[style] then
error("Style was alread added to app")
@@ -82,14 +118,83 @@ function Application:new(arg1, arg2)
end
end
end
- self.appName = appName
- self.damageReports = nil
+ self.appName = appName
+ self.postprocessNeeded = {}
+ self._animations = lwtk.Animations(self)
+ local animationTimer = self._animations.timer
+ self.animationTimer = animationTimer
+
+ getFontInfos[self] = FontInfos(self.driver:getLayoutContext())
- getFontInfos[self] = FontInfos(self.driver:getLayoutContext())
+ self:setAttributes(initParams)
- createClosures(self)
+ driver:setProcessFunc(function()
+ local now = driver:getTime()
+ local closed = isClosed[self]
+ while not closed do
+ local timer = timers[1]
+ if not timer or timer.time > now then
+ break
+ end
+ remove(timers, 1)
+ if timer == animationTimer then
+ self:_processAllChanges()
+ end
+ timer.time = false
+ timer.func(unpack(timer))
+ closed = isClosed[self]
+ end
+ local timer = timers[1]
+ if timer then
+ local t = timer.time - now
+ t = (t >= 0) and t or 0
+ if not closed then
+ driver:setNextProcessTime(t)
+ end
+ end
+ if not closed and not animationTimer.time then
+ self:_processAllChanges()
+ end
+ end)
- self:setAttributes(initParams)
+
+ self._eventFunc = function(window, view, event, ...)
+ --print(event, ...)
+ if event == "CONFIGURE" then
+ window:_handleConfigure(...)
+ elseif event == "EXPOSE" then
+ window:_handleExpose(...)
+ elseif event == "MOTION" then
+ window:_handleMouseMove(...)
+ elseif event == "POINTER_OUT" then
+ window:_handleMouseLeave(...)
+ elseif event == "POINTER_IN" then
+ window:_handleMouseEnter(...)
+ elseif event == "KEY_PRESS" then
+ window:_handleKeyDown(...)
+ elseif event == "KEY_RELEASE" then
+ window:_handleKeyUp(...)
+ elseif event == "BUTTON_PRESS" then
+ window:_handleMouseDown(...)
+ elseif event == "BUTTON_RELEASE" then
+ window:_handleMouseUp(...)
+ elseif event == "FOCUS_IN" then
+ window:_handleFocusIn(...)
+ elseif event == "FOCUS_OUT" then
+ window:_handleFocusOut(...)
+ elseif event == "SCROLL" then
+ window:_handleMouseScroll(...)
+ elseif event == "CLOSE" then
+ window:requestClose()
+ elseif event == "MAP" then
+ window:_handleMap(...)
+ elseif event == "CREATE" then
+ return
+ end
+ if not isClosed[self] and not animationTimer.time then
+ self:_processAllChanges()
+ end
+ end
end
function Application:close()
@@ -162,219 +267,159 @@ function Application:hasWindows()
end
function Application:_addWindow(win)
- self[#self + 1] = win
+ rawset(self, #self + 1, win)
end
function Application:_removeWindow(win)
- for i = 1, #self do
+ for i = #self, 1, -1 do
if self[i] == win then
table.remove(self, i)
end
end
end
-local insert = table.insert
-local remove = table.remove
+function Application:setTimer(seconds, func, ...)
+ local driver = self.driver
+ local timers = self.timers
-local function unpack2(t, i, n)
- if i <= n then
- return t[i], unpack2(t, i + 1, n)
- end
-end
-local function unpack(t)
- return unpack2(t, 1, t.n)
-end
-
-createClosures = function(app)
-
- local driver = app.driver
- local deferredChanges = getDeferredChanges[app]
- local timers = {}
-
- function app:setTimer(seconds, func, ...)
- local n = #timers
- local timer
- if type(func) == "table" then
- timer = func
- if timer.time then
- for i = 1, n do
- if timers[i] == timer then
- remove(timers, i)
- n = n - 1
- break
- end
+ local n = #timers
+ local timer
+ if type(func) == "table" then
+ timer = func
+ if timer.time then
+ for i = 1, n do
+ if timers[i] == timer then
+ remove(timers, i)
+ n = n - 1
+ break
end
end
- else
- assert(type(func) == "function", "Timer object or function expected")
- timer = Timer(func, ...)
end
- local now = driver:getTime()
- local time = now + seconds
- timer.time = time
- for i = 1, n do
- if timers[i].time > time then
- insert(timers, i, timer)
- local t = timers[1].time - now
- t = (t >= 0) and t or 0
- driver:setNextProcessTime(t)
- return timer
- end
+ else
+ assert(type(func) == "function", "Timer object or function expected")
+ timer = Timer(func, ...)
+ end
+ local now = driver:getTime()
+ local time = now + seconds
+ timer.time = time
+ for i = 1, n do
+ if timers[i].time > time then
+ insert(timers, i, timer)
+ local t = timers[1].time - now
+ t = (t >= 0) and t or 0
+ driver:setNextProcessTime(t)
+ return timer
end
- timers[n + 1] = timer
- local t = timers[1].time - now
- t = (t >= 0) and t or 0
- driver:setNextProcessTime(t)
- return timer
end
+ timers[n + 1] = timer
+ local t = timers[1].time - now
+ t = (t >= 0) and t or 0
+ driver:setNextProcessTime(t)
+ return timer
+end
- local animations = lwtk.Animations(app)
- local animationTimer = animations.timer
- app._animations = animations
+function Application:getCurrentTime()
+ return self.driver:getTime()
+end
- function app:getCurrentTime()
- return driver:getTime()
- end
-
- local procssingDeferedChanges = false
- local postprocessNeeded = {}
-
- function app:deferChanges(callback)
- assert(not procssingDeferedChanges)
- deferredChanges[#deferredChanges + 1] = callback
- app._hasChanges = true
- end
-
- local function _processAllChanges(self)
- if app._hasChanges then
- local visibilityChanges = getVisibilityChanges[app]
- for widget, hidden in pairs(visibilityChanges) do
- widget:onEffectiveVisibilityChanged(hidden)
- visibilityChanges[widget] = nil
- end
- procssingDeferedChanges = true
- for i = 1, #deferredChanges do
- deferredChanges[i]:call()
- deferredChanges[i] = nil
- end
- procssingDeferedChanges = false
- app._hasChanges = false
- for _, w in ipairs(app) do
- if w._hasChanges then
- if w:_processChanges() then
- postprocessNeeded[#postprocessNeeded + 1] = w
- end
- assert(not w._hasChanges)
- end
- end
- assert(not app._hasChanges)
+function Application:deferChanges(callback)
+ assert(not self.procssingDeferedChanges)
+ local deferredChanges = getDeferredChanges[self]
+ deferredChanges[#deferredChanges + 1] = callback
+ self._hasChanges = true
+end
+
+function Application:_processAllChanges()
+ local postprocessNeeded = self.postprocessNeeded
+ if self._hasChanges then
+ local visibilityChanges = getVisibilityChanges[self]
+ for widget, hidden in pairs(visibilityChanges) do
+ widget:onEffectiveVisibilityChanged(hidden)
+ visibilityChanges[widget] = nil
end
- for i = 1, #postprocessNeeded do
- postprocessNeeded[i]:_postProcessChanges()
- postprocessNeeded[i] = nil
+ self.procssingDeferedChanges = true
+ local deferredChanges = getDeferredChanges[self]
+ for i = 1, #deferredChanges do
+ deferredChanges[i]()
+ deferredChanges[i] = nil
end
- end
- app._processAllChanges = _processAllChanges
-
- if driver.handleNextEvents then
- function app:runEventLoop(timeout)
- local endTime = timeout and (driver:getTime() + timeout)
- if not animationTimer.time then
- _processAllChanges()
- end
- while app:hasWindows() do
- driver:handleNextEvents(endTime and driver:getTime() - endTime)
- if not isClosed[app] and not animationTimer.time then
- _processAllChanges()
- end
- if endTime and driver:getTime() >= endTime then
- break
+ self.procssingDeferedChanges = false
+ self._hasChanges = false
+ for i = 1, #self do
+ local w = self[i]
+ if w._hasChanges then
+ if w:_processChanges() then
+ postprocessNeeded[#postprocessNeeded + 1] = w
end
+ assert(not w._hasChanges)
end
end
- else
- function app:runEventLoop(timeout)
- error("method 'runEventLoop' not supported")
- end
+ assert(not self._hasChanges)
end
-
+ for i = 1, #postprocessNeeded do
+ postprocessNeeded[i]:_postProcessChanges()
+ postprocessNeeded[i] = nil
+ end
+end
+
+--[[
+ Update by processing events from the window system.
+
+ * *timeout* - optional float, timeout in seconds
+
+ If *timeout* is given, this function will process events from the window system until
+ the time period in seconds has elapsed or until all window objects have been closed.
+
+ If *timeout* is `nil` or not given, this function will process events from the window system
+ until all window objects have been closed.
+]]
+function Application:runEventLoop(timeout)
+ local driver = self.driver
if driver.handleNextEvents then
- function app:update(timeout)
- if not animationTimer.time then
- _processAllChanges()
- end
- return driver:handleNextEvents(timeout)
+ local endTime = timeout and (driver:getTime() + timeout)
+ if not self.animationTimer.time then
+ self:_processAllChanges()
end
- end
-
- driver:setProcessFunc(function()
- local now = driver:getTime()
- local closed = isClosed[app]
- while not closed do
- local timer = timers[1]
- if not timer or timer.time > now then
- break
+ while self:hasWindows() do
+ driver:handleNextEvents(endTime and driver:getTime() - endTime)
+ if not isClosed[self] and not self.animationTimer.time then
+ self:_processAllChanges()
end
- remove(timers, 1)
- if timer == animationTimer then
- _processAllChanges()
- end
- timer.time = false
- timer.func(unpack(timer))
- closed = isClosed[app]
- end
- local timer = timers[1]
- if timer then
- local t = timer.time - now
- t = (t >= 0) and t or 0
- if not closed then
- driver:setNextProcessTime(t)
+ if endTime and driver:getTime() >= endTime then
+ break
end
end
- if not closed and not animationTimer.time then
- _processAllChanges()
- end
- end)
+ else
+ error("method 'runEventLoop' not supported")
+ end
+end
+
+--[[
+ Update by processing events from the window system.
+
+ * *timeout* - optional float, timeout in seconds
+
+ If *timeout* is given, this function will wait for *timeout* seconds until
+ events from the window system become available. If *timeout* is `nil` or not
+ given, this function will block indefinitely until an event occurs.
+
+ As soon as events are available, all events in the queue are processed and this function
+ returns `true`.
- function app._eventFunc(window, view, event, ...)
- --print(event, ...)
- if event == "CONFIGURE" then
- window:_handleConfigure(...)
- elseif event == "EXPOSE" then
- window:_handleExpose(...)
- elseif event == "MOTION" then
- window:_handleMouseMove(...)
- elseif event == "POINTER_OUT" then
- window:_handleMouseLeave(...)
- elseif event == "POINTER_IN" then
- window:_handleMouseEnter(...)
- elseif event == "KEY_PRESS" then
- window:_handleKeyDown(...)
- elseif event == "KEY_RELEASE" then
- window:_handleKeyUp(...)
- elseif event == "BUTTON_PRESS" then
- window:_handleMouseDown(...)
- elseif event == "BUTTON_RELEASE" then
- window:_handleMouseUp(...)
- elseif event == "FOCUS_IN" then
- window:_handleFocusIn(...)
- elseif event == "FOCUS_OUT" then
- window:_handleFocusOut(...)
- elseif event == "SCROLL" then
- window:_handleMouseScroll(...)
- elseif event == "CLOSE" then
- window:_handleClose()
- elseif event == "MAP" then
- window:_handleMap(...)
- elseif event == "CREATE" then
- return
- end
- if not isClosed[app] and not animationTimer.time then
- _processAllChanges()
+ If *timeout* is given and there are no events available after *timeout*
+ seconds, this function will return `false`.
+]]
+function Application:update(timeout)
+ local driver = self.driver
+ if driver.handleNextEvents then
+ if not self.animationTimer.time then
+ self:_processAllChanges()
end
+ return driver:handleNextEvents(timeout)
end
end
+
return Application
diff --git a/src/lwtk/Area.lua b/src/lwtk/Area.lua
index bd29dcb..a7650c1 100644
--- a/src/lwtk/Area.lua
+++ b/src/lwtk/Area.lua
@@ -1,16 +1,35 @@
local lwtk = require"lwtk"
local Rect = lwtk.Rect
+
+
+--[[
+ A list of rectangle coordinates forming an area.
+]]
local Area = lwtk.newClass("lwtk.Area")
local rawget = rawget
local areRectsIntersected = Rect.areRectsIntersected
local doesRectContain = Rect.doesRectContain
+Area:declare(
+ "count" -- number of rectangles in the area
+)
+
+--[[
+ Creates an empty area object.
+]]
function Area:new()
self.count = 0
end
+--[[
+ Obtain coordinates of the i-th rectangle from the area.
+
+ * *i* - index of the rectangle, *1 <= i <= area.count*
+
+ Returns *x, y, w, h* rectangle coordinates
+]]
function Area:getRect(i)
local i0 = (i - 1) * 4
return rawget(self, i0 + 1),
@@ -29,10 +48,22 @@ local function iterator(self, i)
end
end
+--[[
+ Iterate through all rectangle coordinates.
+
+ Returns an *iterator function*, *self* and *0*, so that the construction
+ ```lua
+ for i, x, y, w, h in area:iteration() do ... end
+ ```
+ will iterate over all rectangle indices and coordinates.
+]]
function Area:iteration()
return iterator, self, 0
end
+--[[
+ Adds the rectangle coordinates to the area.
+]]
function Area:addRect(x, y, w, h)
if w > 0 and h > 0 then
for i, x2, y2, w2, h2 in iterator, self, 0 do
@@ -57,6 +88,10 @@ function Area:addRect(x, y, w, h)
end
end
+--[[
+ Returns *true* if the given rectangle coordinates intersect
+ the area.
+]]
function Area:intersects(x, y, w, h)
for _, x2, y2, w2, h2 in iterator, self, 0 do
if areRectsIntersected(x, y, w, h, x2, y2, w2, h2) then
@@ -66,6 +101,10 @@ function Area:intersects(x, y, w, h)
return false
end
+--[[
+ Returns *true* if the given rectangle coordinates are
+ within the area.
+]]
function Area:isWithin(x, y, w, h)
for _, x2, y2, w2, h2 in iterator, self, 0 do
if not doesRectContain(x, y, w, h, x2, y2, w2, h2) then
@@ -90,7 +129,10 @@ function Area:intersectsBorder(x, y, w, h, borderWidth)
return false
end
-
+--[[
+ Clears all rectangle coordinates. After this the
+ area does not contain any rectangle, i.e. *area.count == 0*.
+]]
function Area:clear()
self.count = 0
end
diff --git a/src/lwtk/Bordered.lua b/src/lwtk/Bordered.lua
deleted file mode 100644
index 50f30a4..0000000
--- a/src/lwtk/Bordered.lua
+++ /dev/null
@@ -1,3 +0,0 @@
-local lwtk = require"lwtk"
-
-return lwtk.WidgetWrapper("lwtk.Bordered", lwtk.Box)
diff --git a/src/lwtk/Box.lua b/src/lwtk/Box.lua
index fa56a4d..87ed06a 100644
--- a/src/lwtk/Box.lua
+++ b/src/lwtk/Box.lua
@@ -3,6 +3,6 @@ local lwtk = require"lwtk"
local Super = lwtk.Control(lwtk.Group)
local Box = lwtk.newClass("lwtk.Box", Super)
-Box.getMeasures = lwtk.LayoutFrame.extra.getMeasures
+Box.implement.getMeasures = lwtk.LayoutFrame.extra.getMeasures
return Box
diff --git a/src/lwtk/Callback.lua b/src/lwtk/Callback.lua
index 95af540..9916d38 100644
--- a/src/lwtk/Callback.lua
+++ b/src/lwtk/Callback.lua
@@ -1,6 +1,10 @@
local lwtk = require("lwtk")
-local Callback = lwtk.newClass("lwtk.Callback")
+--[[
+ Holds a function with arguments that can
+ be called.
+]]
+local Callback = lwtk.newMeta("lwtk.Callback")
function Callback:new(func, ...)
@@ -20,7 +24,7 @@ local function unpack(t, i, n, ...)
end
end
-function Callback:call(...)
+function Callback:__call(...)
self.func(unpack(self, 1, self.n, ...))
end
diff --git a/src/lwtk/ChildLookup.lua b/src/lwtk/ChildLookup.lua
index 3158066..b8b5959 100644
--- a/src/lwtk/ChildLookup.lua
+++ b/src/lwtk/ChildLookup.lua
@@ -1,20 +1,19 @@
local lwtk = require"lwtk"
-local getWrapper = lwtk.get.wrapper
local getChildLookup = lwtk.get.childLookup
-local ChildLookup = lwtk.newClass("lwtk.ChildLookup")
+local ChildLookup = lwtk.newMeta("lwtk.ChildLookup")
ChildLookup.__mode = "v"
function ChildLookup:new(group)
- self[0] = false
+ self[0] = false
self[-1] = group
end
-function ChildLookup.__index(self, id)
+function ChildLookup:__index(id)
assert(type(id) == "string", "id must be string")
- local group = self[-1]
+ local group = rawget(self, -1)
local found = nil
if group then
for i = 1, #group do
@@ -34,7 +33,6 @@ function ChildLookup.__index(self, id)
end
end
if found ~= nil then
- found = getWrapper[found] or found
self[0] = true
self[id] = found
end
diff --git a/src/lwtk/Class.lua b/src/lwtk/Class.lua
index d2d5169..44c2b98 100644
--- a/src/lwtk/Class.lua
+++ b/src/lwtk/Class.lua
@@ -1,13 +1,248 @@
-local Class = {
- __name = "lwtk.Class",
- __call = function(class, ...)
- local obj = setmetatable({}, class)
- local new = class.new
- if new then new(obj, ...) end
- return obj
- end,
- __tostring = function(class)
- return class.__name
- end
-}
+local lwtk = require"lwtk"
+
+local rawget = rawget
+local match = string.match
+
+local getObjectMeta = lwtk.get.objectMeta
+local getSuperClass = lwtk.get.superClass
+local getClass = lwtk.get.class
+
+local DO_CHECKS = not _G.LWTK_DISABLE_CHECKS
+
+--[[
+ Metatable for objects created by lwtk.newClass().
+
+ See [lwtk.Class Usage](../../Class.md) for detailed documentation
+ and usage examples.
+]]
+local Class = {}
+
+Class.__name = "lwtk.Class"
+
+function Class:__tostring()
+ if self.__name then
+ return "lwtk.Class<"..self.__name..">"
+ else
+ return "lwtk.Class"
+ end
+end
+
+
+local metaCall = lwtk.Meta.__call
+
+function Class:__call(...)
+ local objMeta = getObjectMeta[self]
+ return metaCall(objMeta, ...)
+end
+
+local staticMeta = {}
+local overrideMeta = {}
+local implementMeta = {}
+
+function Class:__index(k)
+ local objMeta = getObjectMeta[self]
+ local v = objMeta[k]
+ if v ~= nil then
+ return v
+ end
+ if k == "extra" then
+ local extra = {}
+ objMeta[k] = extra
+ return extra
+ elseif k == "static" then
+ local static = {}
+ getObjectMeta[static] = objMeta
+ objMeta[k] = static
+ return setmetatable(static, staticMeta)
+ elseif k == "override" then
+ local override = {}
+ getObjectMeta[override] = objMeta
+ objMeta[k] = override
+ return setmetatable(override, overrideMeta)
+ elseif k == "implement" then
+ local implement = {}
+ getObjectMeta[implement] = objMeta
+ objMeta[k] = implement
+ return setmetatable(implement, implementMeta)
+ end
+ if DO_CHECKS then
+ lwtk.errorf("no member %q in class %s", k, self:getClassPath())
+ end
+end
+
+local function raiseAlreadyDeclaredError(class, objMeta, k, msg)
+ local c = getSuperClass[class]
+ local m = getObjectMeta[c]
+ while c do
+ if m.declared[k] then
+ msg = msg or ""
+ if objMeta[k] == false then
+ lwtk.errorf("member %q from class %q is already declared%s in superclass %q in class path %s", k, objMeta.__name, msg, m.__name, class:getClassPath())
+ else
+ lwtk.errorf("member %q from class %q is already defined%s in superclass %q in class path %s", k, objMeta.__name, msg, m.__name, class:getClassPath())
+ end
+ end
+ c = getSuperClass[c]
+ m = getObjectMeta[c]
+ end
+end
+
+function Class:__newindex(k, v)
+ if k == "declared" or k == "static" or k == "override" or k == "implement" then
+ lwtk.errorf("member name %q no allowed", k)
+ end
+ local objMeta = getObjectMeta[self]
+ local declared = objMeta.declared
+ if DO_CHECKS and declared[k] then
+ if objMeta[k] == false then
+ lwtk.errorf("member %q already declared in class %s ", k, self:getClassPath())
+ else
+ lwtk.errorf("member %q already defined in class %s ", k, self:getClassPath())
+ end
+ end
+ if DO_CHECKS and k ~= "extra" and objMeta[k] ~= nil then
+ raiseAlreadyDeclaredError(self, objMeta, k)
+ end
+ declared[k] = true
+ if match(k, "^__") or k == "extra" or k == "new" then
+ assert(k ~= "__index" and k ~= "__newindex" and k ~= "__name")
+ -- accessible only from class, not from object
+ objMeta[k] = v
+ else
+ local index = objMeta.__index
+ -- accessible from object and class
+ index[k] = v
+ objMeta[k] = v
+ end
+end
+
+local function cnext(t, i)
+ local k, v = next(t, i)
+ if k == "__metatable" then
+ return next(t, k)
+ else
+ return k, v
+ end
+end
+
+function Class:__pairs()
+ return cnext, getObjectMeta[self], nil
+end
+
+function staticMeta:__index(k)
+ local objMeta = getObjectMeta[self]
+ local v = objMeta[k]
+ if v and rawget(objMeta.__index, k) == nil then
+ return v
+ end
+end
+
+function staticMeta:__newindex(k, v)
+ local objMeta = getObjectMeta[self]
+ if k == "declared" or k == "static" or k == "override" or k == "implement" then
+ lwtk.errorf("member name %q no allowed", k)
+ end
+ local declared = objMeta.declared
+ if DO_CHECKS and declared[k] then
+ if objMeta[k] == false then
+ lwtk.errorf("member %q already declared in class %s ", k, getClass[objMeta]:getClassPath())
+ else
+ lwtk.errorf("member %q already defined in class %s ", k, getClass[objMeta]:getClassPath())
+ end
+ end
+ declared[k] = true
+ assert(k ~= "__index" and k ~= "__newindex" and k ~= "__name")
+ if DO_CHECKS and rawget(objMeta.__index, k) ~= nil then
+ raiseAlreadyDeclaredError(getClass[objMeta], objMeta, k, " as non static")
+ end
+ objMeta[k] = v
+end
+
+function overrideMeta:__newindex(k, v)
+ if k == "declared" or k == "static" or k == "override" or k == "implement" then
+ lwtk.errorf("member name %q no allowed", k)
+ end
+ local objMeta = getObjectMeta[self]
+ local declared = objMeta.declared
+ if DO_CHECKS and declared[k] then
+ if objMeta[k] == false then
+ lwtk.errorf("member %q already declared in class %s ", k, getClass[objMeta]:getClassPath())
+ else
+ lwtk.errorf("member %q already defined in class %s ", k, getClass[objMeta]:getClassPath())
+ end
+ end
+ if objMeta[k] == nil then
+ lwtk.errorf("cannot override member %q in class %q because it is not declared in class path %s", k, objMeta.__name, getClass[objMeta]:getClassPath())
+ end
+ declared[k] = true
+ if match(k, "^__") or k == "extra" or k == "new" then
+ assert(k ~= "__index" and k ~= "__newindex" and k ~= "__name")
+ -- accessible only from class, not from object
+ objMeta[k] = v
+ else
+ local index = objMeta.__index
+ if DO_CHECKS and rawget(index, k) == nil then
+ raiseAlreadyDeclaredError(getClass[objMeta], objMeta, k, " as static")
+ end
+ -- accessible from object and class
+ index[k] = v
+ objMeta[k] = v
+ end
+end
+
+function overrideMeta:__index(k)
+ local objMeta = getObjectMeta[self]
+ local v = objMeta[k]
+ if v and objMeta.declared[k] then
+ return v
+ end
+end
+
+function implementMeta:__index(k)
+ local objMeta = getObjectMeta[self]
+ local v = objMeta[k]
+ if v and objMeta.declared[k] then
+ local s = getSuperClass[getClass[objMeta]]
+ if not s or getObjectMeta[k] == nil then
+ return v
+ end
+ end
+end
+
+function implementMeta:__newindex(k, v)
+ if k == "declared" or k == "static" or k == "override" or k == "implement" then
+ lwtk.errorf("member name %q no allowed", k)
+ end
+ local objMeta = getObjectMeta[self]
+ local declared = objMeta.declared
+ if DO_CHECKS and declared[k] then
+ if objMeta[k] == false then
+ lwtk.errorf("member %q already declared in class %s ", k, getClass[objMeta]:getClassPath())
+ else
+ lwtk.errorf("member %q already defined in class %s ", k, getClass[objMeta]:getClassPath())
+ end
+ end
+ if DO_CHECKS and objMeta[k] ~= false then
+ if objMeta[k] == nil then
+ lwtk.errorf("cannot implement member %q in class %q because it is not declared in class path %s", k, objMeta.__name, getClass[objMeta]:getClassPath())
+ else
+ raiseAlreadyDeclaredError(getClass[objMeta], objMeta, k)
+ end
+ end
+ declared[k] = true
+ if match(k, "^__") or k == "extra" or k == "new" then
+ assert(k ~= "__index" and k ~= "__newindex" and k ~= "__name")
+ -- accessible only from class, not from object
+ objMeta[k] = v
+ else
+ local index = objMeta.__index
+ if rawget(index, k) == nil then
+ raiseAlreadyDeclaredError(getClass[objMeta], objMeta, k, " as static")
+ end
+ -- accessible from object and class
+ index[k] = v
+ objMeta[k] = v
+ end
+end
+
return Class
diff --git a/src/lwtk/Color.lua b/src/lwtk/Color.lua
index aa8ff95..7896f19 100644
--- a/src/lwtk/Color.lua
+++ b/src/lwtk/Color.lua
@@ -2,8 +2,14 @@ local format = string.format
local floor = math.floor
local lwtk = require"lwtk"
-local vivid = lwtk.vivid
-
+local vivid = lwtk.internal.vivid
+
+--[[
+ RGBA Color value.
+
+ Contains the float values *r (red)*, *g (green)*, *b (blue)* and optional
+ *a (alpha value)* in the range >= 0 and <= 1.
+]]
local Color = lwtk.newClass("lwtk.Color")
local isInstanceOf = lwtk.isInstanceOf
@@ -13,6 +19,41 @@ local hexBytePat = "("..hexCharPat..hexCharPat..")"
local rgbPat = "^"..hexBytePat..hexBytePat..hexBytePat.."$"
local rgbaPat = "^"..hexBytePat..hexBytePat..hexBytePat..hexBytePat.."$"
+Color:declare(
+ "r", -- red color float value (0 <= r <= 1)
+ "g", -- green color float value (0 <= g <= 1)
+ "b", -- blue color float value (0 <= b <= 1)
+ "a" -- alpha float value (0 <= a <= 1) or nil
+)
+
+--[[
+ Creates a new RGBA color value.
+
+ Possible invocations:
+
+ * **`Color()`**
+
+ If called without arguments, the color black is created, i.e.
+ *r = g = b = 0*.
+
+ * **`Color(r,g,b,a)`**
+
+ If more than one arg is given, arguments are the float color
+ values *r*, *g*, *b*, *a* with the alpha value *a* being optional.
+
+ * **`Color(table)`**
+
+ * *table* - a table with the entries for the keys *r*, *g*, *b*
+ with optional alpha value *a*.
+ Missing entries for *r*, *g*, *b* are treated as 0.
+
+ * **`Color(string)`**
+
+ * *string* - a string in hex encoding with length 6 or 8
+ (format *"rrggbb"* or *"rrggbbaa"*). Each color value
+ consists of two hexadecimal digits and is in the range
+ *00 <= value <= ff*, alpha value is optional.
+]]
function Color:new(...)
local nargs = select("#", ...)
if nargs == 0 then
@@ -122,11 +163,14 @@ function Color.__add(color1, color2)
end
function Color.__eq(color1, color2)
- return floor(color1.r * 0xff) == floor(color2.r * 0xff)
+ return color1.r and color2.r
+ and color1.g and color2.g
+ and color1.b and color2.b
+ and floor(color1.r * 0xff) == floor(color2.r * 0xff)
and floor(color1.g * 0xff) == floor(color2.g * 0xff)
and floor(color1.b * 0xff) == floor(color2.b * 0xff)
- and ( (color1.a == nil and color2.a == nil)
- or (color1.a ~= nil and color2.a ~= nil
+ and ( (not color1.a and not color2.a)
+ or (color1.a and color2.a
and floor(color1.a * 0xff) == floor(color2.a * 0xff)))
end
diff --git a/src/lwtk/Colored.lua b/src/lwtk/Colored.lua
index ed56b04..48c1cb8 100644
--- a/src/lwtk/Colored.lua
+++ b/src/lwtk/Colored.lua
@@ -1,20 +1,21 @@
local lwtk = require("lwtk")
-local Colored = lwtk.newMixin("lwtk.Colored", lwtk.Styleable.NO_STYLE_SELECTOR)
+local Colored = lwtk.newMixin("lwtk.Colored", lwtk.Styleable.NO_STYLE_SELECTOR,
-function Colored.initClass(Colored, Super) -- luacheck: ignore 431/Colored
-
- local Super_onDraw = Super.onDraw
-
- function Colored:onDraw(ctx, ...)
- local background = self:getStyleParam("BackgroundColor")
- if background then
- ctx:fillRect(background, 0, 0, self.w, self.h)
- end
- if Super_onDraw then
- Super_onDraw(self, ctx, ...)
+ function(Colored, Super)
+
+ local Super_onDraw = Super.onDraw
+
+ function Colored.implement:onDraw(ctx, ...)
+ local background = self:getStyleParam("BackgroundColor")
+ if background then
+ ctx:fillRect(background, 0, 0, self.w, self.h)
+ end
+ if Super_onDraw then
+ Super_onDraw(self, ctx, ...)
+ end
end
end
-end
+)
return Colored
diff --git a/src/lwtk/Column.lua b/src/lwtk/Column.lua
index 9eaa636..3f207ed 100644
--- a/src/lwtk/Column.lua
+++ b/src/lwtk/Column.lua
@@ -5,7 +5,17 @@ local Column = lwtk.newClass("lwtk.Column", Super)
-------------------------------------------------------------------------------------------------
-lwtk.internal.ColumnImpl.implementColumn(Column, false)
+local impl = lwtk.internal.ColumnImpl.implementColumn(false)
+
+--[[
+ @function(self)
+]]
+Column.implement.getMeasures = impl.getMeasures
+
+--[[
+ @function(self, width, height, isLayoutTransition)
+]]
+Column.implement.onLayout = impl.onLayout
-------------------------------------------------------------------------------------------------
diff --git a/src/lwtk/Component.lua b/src/lwtk/Component.lua
index 911c482..9d168b3 100644
--- a/src/lwtk/Component.lua
+++ b/src/lwtk/Component.lua
@@ -21,21 +21,45 @@ local callOnLayout = lwtk.layout.callOnLayout
local getFontInfos = lwtk.get.fontInfos
local getChildLookup = lwtk.get.childLookup
-local Super = lwtk.Actionable
+local Super = lwtk.Drawable(lwtk.Node(lwtk.Actionable()))
local Component = lwtk.newClass("lwtk.Component", Super)
-function Component:new(initParams)
- getApp[self] = false
- getRoot[self] = self
- self.visible = true
+Component:declare(
+ "_animationActive",
+ "_animationTriggered",
+ "_handleFocusIn",
+ "_handleHasFocusHandler",
+ "_hasChanges",
+ "_isRelayouting",
+ "_needsRedraw",
+ "_needsRelayout",
+ "_positionsChanged",
+ "_frameTransition",
+ "_hidden",
+ "_ignored",
+ "initParams",
+ "oldX", "oldY", "oldW", "oldH",
+ "getMeasures",
+ "onDisabled",
+ "onDraw",
+ "onHotkeyEnabled",
+ "onEffectiveVisibilityChanged",
+ "onLayout",
+ "onRealize"
+)
+
+function Component.override:new(initParams)
self.x = 0
self.y = 0
self.w = 0
self.h = 0
+ self.visible = true
+ getApp[self] = false
+ getRoot[self] = self
Super.new(self, initParams)
end
-function Component:setInitParams(initParams)
+function Component.override:setInitParams(initParams)
if initParams then
local id = initParams.id
if id then
@@ -57,7 +81,7 @@ function Component:setInitParams(initParams)
end
end
-function Component:handleRemainingInitParams(initParams)
+function Component.override:handleRemainingInitParams(initParams)
local hasRemaining = false
for k, v in pairs(initParams) do
assert(type(k) == "string", "attribute names must be string")
@@ -78,14 +102,11 @@ end
function Component:setTimer(seconds, func, ...)
local p = assert(getParent[self], "Component not connected to parent")
p:setTimer(seconds, func, ...)
- self.setTimer = p.setTimer
end
function Component:getCurrentTime()
- local p = assert(getParent[self], "Component not connected to parent")
- local rslt = p:getCurrentTime()
- self.getCurrentTime = p.getCurrentTime
- return rslt
+ local app = assert(getApp[self], "Component not connected to Application")
+ return app:getCurrentTime()
end
function Component:getFontInfo(family, slant, weight, size)
@@ -141,7 +162,8 @@ local function setAppAndRoot(self, app, root)
if app then
self:_setApp(app)
end
- for _, child in ipairs(self) do
+ for i = 1, #self do
+ local child = rawget(self, i)
setAppAndRoot(child, app, root)
end
if (self._handleFocusIn or self.onHotkeyEnabled) and not getFocusHandler[self] then
@@ -180,7 +202,8 @@ end
function Component:_setParent(parent)
assert(not getParent[self], "Component was already added to parent")
getParent[self] = parent
- setAppAndRoot(self, getApp[parent],
+ local app = getApp[parent]
+ setAppAndRoot(self, app,
getRoot[parent])
local _needsRelayout = self._needsRelayout
if self._hasChanges then
@@ -192,6 +215,9 @@ function Component:_setParent(parent)
w._hasChanges = true
w._needsRelayout = _needsRelayout
w = getParent[w]
+ if not w and app then
+ app._hasChanges = true
+ end
until not w
end
end
@@ -259,6 +285,12 @@ function Component:_setFrame(newX, newY, newW, newH, fromFrameAnimation)
end
widget._hasChanges = true
widget = getParent[widget]
+ if not widget then
+ local app = getApp[self]
+ if app then
+ app._hasChanges = true
+ end
+ end
until not widget
end
self.x, self.y, self.w, self.h = newX, newY, newW, newH
@@ -342,13 +374,17 @@ end
function Component:triggerLayout()
- if getApp[self] then
+ local app = getApp[self]
+ if app then
self._needsRelayout = true
local p = getParent[self]
while p and not p._needsRelayout do
p._hasChanges = true
p._needsRelayout = true
p = getParent[p]
+ if not p then
+ app._hasChanges = true
+ end
end
end
end
@@ -363,6 +399,12 @@ function Component:triggerRedraw()
end
w._hasChanges = true
w = getParent[w]
+ if not w then
+ local app = getApp[self]
+ if app then
+ app._hasChanges = true
+ end
+ end
until not w
end
end
@@ -390,14 +432,6 @@ function Component:updateAnimation()
getParent[self]:updateAnimation()
end
-function Component:getStyleParam(paramName)
- return getParent[self]:getStyleParam(paramName)
-end
-
-function Component:getMandatoryStyleParam(paramName)
- return getParent[self]:getMandatoryStyleParam(paramName)
-end
-
function Component:_processDraw(ctx, x0, y0, cx, cy, cw, ch, exposedArea)
local onDraw = self.onDraw
if onDraw then
@@ -413,42 +447,4 @@ function Component:_processDraw(ctx, x0, y0, cx, cy, cw, ch, exposedArea)
end
end
-function Component:_processMouseEnter(x, y)
- call("onMouseEnter", self, x, y)
-end
-
-function Component:_processMouseMove(mouseEntered, x, y)
- call("onMouseMove", self, x, y)
-end
-
-function Component:_processMouseLeave(x, y)
- call("onMouseLeave", self, x, y)
-end
-
-function Component:_processMouseScroll(dx, dy)
- local comp = self
- while true do
- local onMouseScroll = comp.onMouseScroll
- if onMouseScroll and onMouseScroll(comp, dx, dy) then
- return true
- end
- comp = getParent[comp]
- if not comp then
- return false
- end
- end
-end
-
-function Component:_processMouseDown(mx, my, button, modState)
- local onMouseDown = self.onMouseDown
- if onMouseDown then
- onMouseDown(self, mx, my, button, modState)
- return true, true
- end
-end
-
-function Component:_processMouseUp(mouseEntered, mx, my, button, modState)
- call("onMouseUp", self, mx, my, button, modState)
-end
-
return Component
diff --git a/src/lwtk/Compound.lua b/src/lwtk/Compound.lua
index 1defdbb..2a6af0b 100644
--- a/src/lwtk/Compound.lua
+++ b/src/lwtk/Compound.lua
@@ -2,35 +2,35 @@ local lwtk = require"lwtk"
local Rect = lwtk.Rect
local intersectRects = Rect.intersectRects
-local getWrappingParent = lwtk.get.wrappingParent
-local Compound = lwtk.newMixin("lwtk.Compound", lwtk.Styleable.NO_STYLE_SELECTOR)
+local Compound = lwtk.newMixin("lwtk.Compound", lwtk.Styleable.NO_STYLE_SELECTOR,
-function Compound.initClass(Compound, Super) -- luacheck: ignore 431/Compound
+ function(Compound, Super)
- function Compound:_processChanges(x0, y0, cx, cy, cw, ch, damagedArea)
- Super._processChanges(self, x0, y0, cx, cy, cw, ch, damagedArea)
- local x, y, w, h = x0 + self.x, y0 + self.y, self.w, self.h
- cx, cy, cw, ch = intersectRects(x, y, w, h, cx, cy, cw, ch)
- for _, child in ipairs(self) do
- if child._hasChanges then
- child._hasChanges = false
- child:_processChanges(x, y, cx, cy, cw, ch, damagedArea)
+ function Compound.override:_processChanges(x0, y0, cx, cy, cw, ch, damagedArea)
+ Super._processChanges(self, x0, y0, cx, cy, cw, ch, damagedArea)
+ local x, y, w, h = x0 + self.x, y0 + self.y, self.w, self.h
+ cx, cy, cw, ch = intersectRects(x, y, w, h, cx, cy, cw, ch)
+ for i = 1, #self do
+ local child = self[i]
+ if child._hasChanges then
+ child._hasChanges = false
+ child:_processChanges(x, y, cx, cy, cw, ch, damagedArea)
+ end
end
end
+
end
-
-end
+)
-function Compound:addChild(child)
- local myChild = getWrappingParent[child] or child
- self[#self + 1] = myChild
- myChild:_setParent(self)
+function Compound.implement:addChild(child)
+ rawset(self, #self + 1, child)
+ child:_setParent(self)
return child
end
-function Compound:_processDraw(ctx, x0, y0, cx, cy, cw, ch, exposedArea)
+function Compound.override:_processDraw(ctx, x0, y0, cx, cy, cw, ch, exposedArea)
local opacity = self:getStyleParam("Opacity") or 1
if opacity < 1 then
ctx:beginOpacity(opacity)
@@ -45,7 +45,8 @@ function Compound:_processDraw(ctx, x0, y0, cx, cy, cw, ch, exposedArea)
cx, cy, cw, ch = intersectRects(x0, y0, self.w, self.h, cx, cy, cw, ch)
if cw > 0 and ch > 0 then
- for _, child in ipairs(self) do
+ for i = 1, #self do
+ local child = self[i]
if not child._ignored then
local childX, childY = child.x, child.y
local x, y, w, h = x0 + childX, y0 + childY, child.w, child.h
diff --git a/src/lwtk/DefaultKeyBinding.lua b/src/lwtk/DefaultKeyBinding.lua
index 2594ee7..afa7131 100644
--- a/src/lwtk/DefaultKeyBinding.lua
+++ b/src/lwtk/DefaultKeyBinding.lua
@@ -1,5 +1,8 @@
local lwtk = require"lwtk"
+--[[
+ Returns new lwtk.KeyBinding object with default settings.
+]]
return function()
return lwtk.KeyBinding
{
diff --git a/src/lwtk/DefaultStyle.lua b/src/lwtk/DefaultStyle.lua
index d16be90..2559235 100644
--- a/src/lwtk/DefaultStyle.lua
+++ b/src/lwtk/DefaultStyle.lua
@@ -7,7 +7,7 @@ local scale = lwtk.StyleRef.scale
local Super = lwtk.Style
local DefaultStyle = lwtk.newClass("lwtk.DefaultStyle", Super)
-function DefaultStyle:new(initParams)
+function DefaultStyle.override:new(initParams)
local function par(name)
local rslt = initParams and initParams[name]
@@ -37,6 +37,7 @@ function DefaultStyle:new(initParams)
{ "TextSize", 12 },
{ "FontFamily", "sans-serif" },
+ { "ScrollBarSize", 12 },
{ "BackgroundColor", backgroundColor },
{ "TextColor", textColor },
@@ -48,10 +49,11 @@ function DefaultStyle:new(initParams)
{ "CursorColor:focused", Color"000000" },
{ "CursorWidth", 2 },
- { "*Margin@Control", 8 },
- { "Height@Control", 24 },
- { "BorderPadding@Control", 3 },
- { "TextFullVisible@Control", true },
+ { "Margin@Control", 8 },
+ { "*Margin@Control", get"Margin" },
+ { "Height@Control", 24 },
+ { "BorderPadding@Control", 3 },
+ { "TextFullVisible@Control", true },
{ "BorderSize@Box", 1 },
{ "BorderPadding@Box", 3 },
diff --git a/src/lwtk/Drawable.lua b/src/lwtk/Drawable.lua
new file mode 100644
index 0000000..1a1c963
--- /dev/null
+++ b/src/lwtk/Drawable.lua
@@ -0,0 +1,67 @@
+local lwtk = require"lwtk"
+
+local call = lwtk.call
+local getParent = lwtk.get.parent
+
+local Drawable = lwtk.newMixin("lwtk.Drawable")
+
+Drawable:declare(
+ "visible",
+ "x", "y", "w", "h",
+ "id",
+ "addChild",
+ "_setStyleFromParent"
+)
+
+function Drawable:getStyleParam(paramName)
+ return getParent[self]:getStyleParam(paramName)
+end
+
+function Drawable:getMandatoryStyleParam(paramName)
+ local p = self:getStyleParam(paramName)
+ if not p then
+ lwtk.errorf("Missing StyleParam %q", paramName)
+ end
+ return p
+end
+
+function Drawable.implement:_processMouseEnter(x, y)
+ call("onMouseEnter", self, x, y)
+end
+
+function Drawable.implement:_processMouseLeave(x, y)
+ call("onMouseLeave", self, x, y)
+end
+
+function Drawable.implement:_processMouseMove(mouseEntered, x, y)
+ call("onMouseMove", self, x, y)
+end
+
+
+function Drawable.implement:_processMouseScroll(dx, dy)
+ local comp = self
+ while true do
+ local onMouseScroll = comp.onMouseScroll
+ if onMouseScroll and onMouseScroll(comp, dx, dy) then
+ return true
+ end
+ comp = getParent[comp]
+ if not comp then
+ return false
+ end
+ end
+end
+
+function Drawable.implement:_processMouseDown(mx, my, button, modState)
+ local onMouseDown = self.onMouseDown
+ if onMouseDown then
+ onMouseDown(self, mx, my, button, modState)
+ return true, true
+ end
+end
+
+function Drawable.implement:_processMouseUp(mouseEntered, mx, my, button, modState)
+ call("onMouseUp", self, mx, my, button, modState)
+end
+
+return Drawable
diff --git a/src/lwtk/FocusGroup.lua b/src/lwtk/FocusGroup.lua
index 358a0bd..7c13ef0 100644
--- a/src/lwtk/FocusGroup.lua
+++ b/src/lwtk/FocusGroup.lua
@@ -10,12 +10,16 @@ local FocusHandler = lwtk.FocusHandler
local Super = lwtk.Focusable(lwtk.Box)
local FocusGroup = lwtk.newClass("lwtk.FocusGroup", Super)
-function FocusGroup:new(...)
+FocusGroup:declare(
+ "entered"
+)
+
+function FocusGroup.override:new(...)
Super.new(self, ...)
getFocusHandler[self] = FocusHandler(self)
end
-function FocusGroup:_setApp(app)
+function FocusGroup.override:_setApp(app)
getApp[getFocusHandler[self]] = app
Super._setApp(self, app)
local parentHandler = getParent[self]:getFocusHandler()
@@ -29,14 +33,14 @@ function FocusGroup:_setApp(app)
end
end
-function FocusGroup:_handleFocusIn()
+function FocusGroup.override:_handleFocusIn()
Super._handleFocusIn(self)
if self.entered then
getFocusHandler[self]:_handleFocusIn()
end
end
-function FocusGroup:_handleFocusOut(reallyLostFocus)
+function FocusGroup.override:_handleFocusOut(reallyLostFocus)
Super._handleFocusOut(self)
getFocusHandler[self]:_handleFocusOut()
if reallyLostFocus then
@@ -45,14 +49,14 @@ function FocusGroup:_handleFocusOut(reallyLostFocus)
end
end
-function FocusGroup:_processMouseDown(mx, my, button, modState)
+function FocusGroup.override:_processMouseDown(mx, my, button, modState)
if button == 1 and not self.disabled then
getParentFocusHandler[self]:setFocusTo(self)
end
return Super._processMouseDown(self, mx, my, button, modState)
end
-function FocusGroup:_handleChildRequestsFocus()
+function FocusGroup.implement:_handleChildRequestsFocus()
if not self.entered then
self.entered = true
self:setState("entered", true)
@@ -69,7 +73,7 @@ function FocusGroup:_handleChildRequestsFocus()
end
end
-function FocusGroup:setFocus(flag)
+function FocusGroup.override:setFocus(flag)
if not self.disabled and (flag == nil or flag) then
local focusHandler = getParentFocusHandler[self]
if focusHandler then
@@ -80,7 +84,7 @@ function FocusGroup:setFocus(flag)
end
end
-function FocusGroup:onKeyDown(key, modifier, ...)
+function FocusGroup.override:onKeyDown(key, modifier, ...)
local handled
if self.entered then
handled = getFocusHandler[self]:onKeyDown(key, modifier, ...)
@@ -88,7 +92,7 @@ function FocusGroup:onKeyDown(key, modifier, ...)
return handled
end
-function FocusGroup:handleHotkey(key, modifier, ...)
+function FocusGroup.implement:handleHotkey(key, modifier, ...)
local handled
if self.entered then
handled = getFocusHandler[self]:handleHotkey(key, modifier, ...)
@@ -96,7 +100,7 @@ function FocusGroup:handleHotkey(key, modifier, ...)
return handled
end
-function FocusGroup:invokeActionMethod(actionMethodName)
+function FocusGroup.override:invokeActionMethod(actionMethodName)
local handled
if self.entered then
handled = getFocusHandler[self]:invokeActionMethod(actionMethodName)
diff --git a/src/lwtk/FocusHandler.lua b/src/lwtk/FocusHandler.lua
index a59b555..d50de7e 100644
--- a/src/lwtk/FocusHandler.lua
+++ b/src/lwtk/FocusHandler.lua
@@ -14,10 +14,16 @@ local getDefault = lwtk.WeakKeysTable()
local getHotkeys = lwtk.WeakKeysTable()
local registeredWidgets = lwtk.WeakKeysTable()
-local Super = lwtk.Actionable
+local Super = lwtk.Actionable()
local FocusHandler = lwtk.newClass("lwtk.FocusHandler", Super)
-function FocusHandler:new(baseWidget)
+FocusHandler:declare(
+ "baseWidget",
+ "_hasFocus",
+ "defaultPostponed"
+)
+
+function FocusHandler.override:new(baseWidget)
Super.new(self)
self.baseWidget = baseWidget
getFocusableChildren[self] = {}
@@ -459,7 +465,7 @@ function FocusHandler:setFocusTo(newFocusChild)
end
end
-function FocusHandler:hasActionMethod(actionMethodName)
+function FocusHandler.override:hasActionMethod(actionMethodName)
local focusedChild = getFocusedChild[self]
if focusedChild then
local childHasActionMethod = focusedChild.hasActionMethod
@@ -470,7 +476,7 @@ function FocusHandler:hasActionMethod(actionMethodName)
return Super.hasActionMethod(self, actionMethodName)
end
-function FocusHandler:invokeActionMethod(actionMethodName)
+function FocusHandler.override:invokeActionMethod(actionMethodName)
local focusedChild = getFocusedChild[self]
if focusedChild then
local childInvokeActionMethod = focusedChild.invokeActionMethod
diff --git a/src/lwtk/Focusable.lua b/src/lwtk/Focusable.lua
index 5412b05..27b45c6 100644
--- a/src/lwtk/Focusable.lua
+++ b/src/lwtk/Focusable.lua
@@ -3,62 +3,75 @@ local lwtk = require"lwtk"
local call = lwtk.call
local getFocusHandler = lwtk.get.focusHandler
-local Focusable = lwtk.newMixin("lwtk.Focusable")
-
-Focusable.extra = {}
-
local handlePostponedStates
-function Focusable.initClass(Focusable, Super) -- luacheck: ignore 431/Focusable
+local Focusable = lwtk.newMixin("lwtk.Focusable",
- function Focusable:_handleHasFocusHandler(focusHandler)
- if Super._handleHasFocusHandler then
- Super._handleHasFocusHandler(self, focusHandler)
- end
- if not self._hidden then
- handlePostponedStates(self, focusHandler)
- end
- end
+ function(Focusable, Super)
- function Focusable:onEffectiveVisibilityChanged(hidden)
- local superCall = Super.onEffectiveVisibilityChanged
- if superCall then
- superCall(self, hidden)
- end
- if not hidden then
- local focusHandler = getFocusHandler[self]
- if focusHandler then
+ Focusable:declare(
+ "_focusDisabled",
+ "_wantsDefault",
+ "_wantsFocus",
+ "_wantsFocusDisabled",
+ "disabled",
+ "onHotkeyDown",
+ "onKeyDown",
+ "hasFocus",
+ "onFocusIn",
+ "onFocusOut",
+ "handleHotkey"
+ )
+
+ function Focusable.override:_handleHasFocusHandler(focusHandler)
+ if Super._handleHasFocusHandler then
+ Super._handleHasFocusHandler(self, focusHandler)
+ end
+ if not self._hidden then
handlePostponedStates(self, focusHandler)
end
- else
- local focusHandler = getFocusHandler[self]
- if focusHandler then
- if self.hasFocus then
- focusHandler:releaseFocus(self)
+ end
+
+ function Focusable.override:onEffectiveVisibilityChanged(hidden)
+ local superCall = Super.onEffectiveVisibilityChanged
+ if superCall then
+ superCall(self, hidden)
+ end
+ if not hidden then
+ local focusHandler = getFocusHandler[self]
+ if focusHandler then
+ handlePostponedStates(self, focusHandler)
end
- local isCurrentDefault, isPrincipalDefault = focusHandler:isDefault(self)
- if isCurrentDefault or isPrincipalDefault then
- focusHandler:setDefault(self, false)
+ else
+ local focusHandler = getFocusHandler[self]
+ if focusHandler then
+ if self.hasFocus then
+ focusHandler:releaseFocus(self)
+ end
+ local isCurrentDefault, isPrincipalDefault = focusHandler:isDefault(self)
+ if isCurrentDefault or isPrincipalDefault then
+ focusHandler:setDefault(self, false)
+ end
end
end
end
- end
-
- local Super_onDisabled = Super.onDisabled
-
- function Focusable:onDisabled(disableFlag)
- local focusHandler = getFocusHandler[self]
- if focusHandler then
- focusHandler:setFocusDisabled(self, disableFlag)
- else
- self._wantsFocusDisabled = true
- end
- if Super_onDisabled then
- Super_onDisabled(self, disableFlag)
+
+ local Super_onDisabled = Super.onDisabled
+
+ function Focusable.override:onDisabled(disableFlag)
+ local focusHandler = getFocusHandler[self]
+ if focusHandler then
+ focusHandler:setFocusDisabled(self, disableFlag)
+ else
+ self._wantsFocusDisabled = true
+ end
+ if Super_onDisabled then
+ Super_onDisabled(self, disableFlag)
+ end
end
+
end
-
-end
+)
handlePostponedStates = function(self, focusHandler)
if self._wantsFocus then
@@ -74,7 +87,7 @@ handlePostponedStates = function(self, focusHandler)
end
end
-function Focusable:_handleFocusIn()
+function Focusable.implement:_handleFocusIn()
self.hasFocus = true
call("onFocusIn", self)
self:setState("focused", true)
diff --git a/src/lwtk/FontInfo.lua b/src/lwtk/FontInfo.lua
index 92a2592..dc98f88 100644
--- a/src/lwtk/FontInfo.lua
+++ b/src/lwtk/FontInfo.lua
@@ -2,6 +2,17 @@ local lwtk = require"lwtk"
local FontInfo = lwtk.newClass("lwtk.FontInfo")
+FontInfo:declare(
+ "ascent",
+ "descent",
+ "family",
+ "height",
+ "layoutContext",
+ "size",
+ "slant",
+ "weight"
+)
+
function FontInfo:new(layoutContext, family, slant, weight, size)
self.layoutContext = layoutContext
self.family = family
diff --git a/src/lwtk/FontInfos.lua b/src/lwtk/FontInfos.lua
index 81ae91d..8be60de 100644
--- a/src/lwtk/FontInfos.lua
+++ b/src/lwtk/FontInfos.lua
@@ -4,6 +4,11 @@ local FontInfo = lwtk.FontInfo
local FontInfos = lwtk.newClass("lwtk.FontInfos")
+FontInfos:declare(
+ "layoutContext",
+ "cache"
+)
+
function FontInfos:new(layoutContext)
self.layoutContext = layoutContext
self.cache = {}
diff --git a/src/lwtk/Group.lua b/src/lwtk/Group.lua
index f56ac19..52bb5e0 100644
--- a/src/lwtk/Group.lua
+++ b/src/lwtk/Group.lua
@@ -7,13 +7,16 @@ local getParent = lwtk.get.parent
local getChildLookup = lwtk.get.childLookup
local getStyle = lwtk.get.style
-
local Super = lwtk.MouseDispatcher(lwtk.Compound(lwtk.Widget))
local Group = lwtk.newClass("lwtk.Group", Super)
+Group:declare(
+ "_handleChildRequestsFocus"
+)
+
local Super_addChild = Super.addChild
-function Group:new(initParams)
+function Group.override:new(initParams)
Super.new(self)
getChildLookup[self] = ChildLookup(self)
local childList = {}
@@ -49,7 +52,7 @@ function Group:childById(id)
end
end
-function Group:addChild(child)
+function Group.override:addChild(child)
Super_addChild(self, child)
self:_clearChildLookup()
local style = getStyle[self]
diff --git a/src/lwtk/HotkeyHandler.lua b/src/lwtk/HotkeyHandler.lua
deleted file mode 100644
index 5655730..0000000
--- a/src/lwtk/HotkeyHandler.lua
+++ /dev/null
@@ -1,11 +0,0 @@
-local lwtk = require"lwtk"
-
-local HotkeyHandler = lwtk.newClass("lwtk.HotkeyHandler")
-
-local getRegistry = setmetatable({}, { __mode = "k" })
-
-function HotkeyHandler:new()
- getRegistry[self] = {}
-end
-
-return HotkeyHandler
diff --git a/src/lwtk/HotkeyListener.lua b/src/lwtk/HotkeyListener.lua
index a1b3346..788edb5 100644
--- a/src/lwtk/HotkeyListener.lua
+++ b/src/lwtk/HotkeyListener.lua
@@ -4,43 +4,44 @@ local getFocusHandler = lwtk.get.focusHandler
local getHotkeys = lwtk.WeakKeysTable()
local registeredHotkeys = lwtk.WeakKeysTable()
-local HotkeyListener = lwtk.newMixin("lwtk.HotkeyListener", lwtk.Styleable.NO_STYLE_SELECTOR)
-
local processHotKeyRegistration
-function HotkeyListener.initClass(HotkeyListener, Super) -- luacheck: ignore 431/HotkeyListener
+local HotkeyListener = lwtk.newMixin("lwtk.HotkeyListener", lwtk.Styleable.NO_STYLE_SELECTOR,
- local Super_onEffectiveVisibilityChanged = Super.onEffectiveVisibilityChanged
-
- function HotkeyListener:onEffectiveVisibilityChanged(hidden)
- if Super_onEffectiveVisibilityChanged then
- Super_onEffectiveVisibilityChanged(self, hidden)
- end
- processHotKeyRegistration(self, not hidden)
- end
+ function(HotkeyListener, Super)
- function HotkeyListener:_handleHasFocusHandler(focusHandler)
- local superCall = Super._handleHasFocusHandler
- if superCall then
- superCall(self, focusHandler)
+ local Super_onEffectiveVisibilityChanged = Super.onEffectiveVisibilityChanged
+
+ function HotkeyListener.implement:onEffectiveVisibilityChanged(hidden)
+ if Super_onEffectiveVisibilityChanged then
+ Super_onEffectiveVisibilityChanged(self, hidden)
+ end
+ processHotKeyRegistration(self, not hidden)
end
- local hotkeys = getHotkeys[self]
- if hotkeys and not self._hidden then
- focusHandler:registerHotkeys(self, hotkeys)
- registeredHotkeys[self] = hotkeys
+
+ function HotkeyListener.implement:_handleHasFocusHandler(focusHandler)
+ local superCall = Super._handleHasFocusHandler
+ if superCall then
+ superCall(self, focusHandler)
+ end
+ local hotkeys = getHotkeys[self]
+ if hotkeys and not self._hidden then
+ focusHandler:registerHotkeys(self, hotkeys)
+ registeredHotkeys[self] = hotkeys
+ end
end
- end
+
+ local Super_onDisabled = Super.onDisabled
- local Super_onDisabled = Super.onDisabled
-
- function HotkeyListener:onDisabled(disableFlag)
- processHotKeyRegistration(self, not disableFlag)
- if Super_onDisabled then
- Super_onDisabled(self, disableFlag)
+ function HotkeyListener.implement:onDisabled(disableFlag)
+ processHotKeyRegistration(self, not disableFlag)
+ if Super_onDisabled then
+ Super_onDisabled(self, disableFlag)
+ end
end
+
end
-
-end
+)
function processHotKeyRegistration(self, registrateFlag)
if registrateFlag then
@@ -104,7 +105,7 @@ function HotkeyListener:isHotkeyEnabled(hotkey)
return hotkeys and hotkeys[hotkey]
end
-function HotkeyListener:onHotkeyEnabled(hotkey)
+function HotkeyListener.implement:onHotkeyEnabled(hotkey)
local hotkeys = getHotkeys[self]
if not hotkeys[hotkey] then
hotkeys[hotkey] = true
diff --git a/src/lwtk/KeyBinding.lua b/src/lwtk/KeyBinding.lua
index 822b8ae..4cbcf27 100644
--- a/src/lwtk/KeyBinding.lua
+++ b/src/lwtk/KeyBinding.lua
@@ -5,7 +5,7 @@ local utf8 = lwtk.utf8
local upper = utf8.upper
local lower = utf8.lower
-local KeyBinding = lwtk.newClass("lwtk.KeyBinding")
+local KeyBinding = lwtk.newMeta("lwtk.KeyBinding")
local function normalizeKeyName(key)
key = string.gsub(key, "^ *(.-) *$", "%1")
diff --git a/src/lwtk/KeyHandler.lua b/src/lwtk/KeyHandler.lua
index 9562dd7..8bb702f 100644
--- a/src/lwtk/KeyHandler.lua
+++ b/src/lwtk/KeyHandler.lua
@@ -11,8 +11,6 @@ local len = utf8.len
local getKeyBinding = lwtk.get.keyBinding
local getFocusHandler = lwtk.get.focusHandler
-local KeyHandler = lwtk.newMixin("lwtk.KeyHandler")
-
local modMap = {}
local caches = {}
local keyMap = {}
@@ -31,16 +29,23 @@ local isModifier = {
Super_R = "Super"
}
-local getState = setmetatable({}, { __mode = "k" })
+local getState = lwtk.WeakKeysTable()
-function KeyHandler.initClass(KeyHandler, Super) -- luacheck: ignore 431/KeyHandler
+local KeyHandler = lwtk.newMixin("lwtk.KeyHandler",
- function KeyHandler:new(...)
- Super.new(self, ...)
- getState[self] = { current = false, mod = false }
- end
+ function(KeyHandler, Super)
-end
+ KeyHandler:declare(
+ "interceptKeyDown"
+ )
+
+ function KeyHandler.override:new(...)
+ Super.new(self, ...)
+ getState[self] = { current = false, mod = false }
+ end
+
+ end
+)
function KeyHandler:resetKeyHandling()
local state = getState[self]
@@ -177,7 +182,7 @@ function KeyHandler:_handleKeyUp(keyName, keyState, keyText)
state.mod = false
keyName = toModKeyString(keyName, keyState)
local keyBinding = getKeyBinding[self]
- local actions = keyBinding[keyName]
+ local actions = keyBinding[keyName]
if actions then
local child = getVisibleChild(self)
if child then
diff --git a/src/lwtk/LayoutFrame.lua b/src/lwtk/LayoutFrame.lua
index 09cf60d..d26cca4 100644
--- a/src/lwtk/LayoutFrame.lua
+++ b/src/lwtk/LayoutFrame.lua
@@ -4,21 +4,20 @@ local getMeasures = lwtk.layout.getMeasures
local setOuterMargins = lwtk.layout.setOuterMargins
-local LayoutFrame = lwtk.newMixin("lwtk.LayoutFrame", lwtk.Styleable.NO_STYLE_SELECTOR)
- LayoutFrame.extra = {}
+local LayoutFrame = lwtk.newMixin("lwtk.LayoutFrame", lwtk.Styleable.NO_STYLE_SELECTOR,
-function LayoutFrame.initClass(LayoutFrame, Super) -- luacheck: ignore 431/LayoutFrame
+ function(LayoutFrame, Super)
- local Super_addChild = Super.addChild
-
- function LayoutFrame:addChild(child)
- if self[1] then
- lwtk.errorf("object of type %s can only have one child", lwtk.type(self))
+ local Super_addChild = Super.addChild
+
+ function LayoutFrame.override:addChild(child)
+ if rawget(self, 1) then
+ lwtk.errorf("object of type %s can only have one child", lwtk.type(self))
+ end
+ return Super_addChild(self, child)
end
- return Super_addChild(self, child)
end
-
-end
+)
function LayoutFrame.extra:getMeasures()
local child = self[1]
@@ -55,7 +54,7 @@ function LayoutFrame.extra:getMeasures()
end
end
-function LayoutFrame:onLayout(w, h)
+function LayoutFrame.implement:onLayout(w, h)
local child = self[1]
if child then
local p = self:getStyleParam("BorderPadding") or 0
@@ -102,7 +101,7 @@ function LayoutFrame:onLayout(w, h)
end
end
-function LayoutFrame:onDraw(ctx, x0, y0, cx, cy, cw, ch, exposedArea)
+function LayoutFrame.implement:onDraw(ctx, x0, y0, cx, cy, cw, ch, exposedArea)
local background = self:getStyleParam("BackgroundColor")
local color = self:getStyleParam("BorderColor")
local b = self:getStyleParam("BorderSize") or 0
diff --git a/src/lwtk/Matrix.lua b/src/lwtk/Matrix.lua
index 6dd141a..468fbea 100644
--- a/src/lwtk/Matrix.lua
+++ b/src/lwtk/Matrix.lua
@@ -12,17 +12,21 @@ local calculateLRMeasures = lwtk.internal.LayoutImpl.calculateLRMeasures
local calculateTBMeasures = lwtk.internal.LayoutImpl.calculateTBMeasures
local applyTBLayout = lwtk.internal.LayoutImpl.applyTBLayout
-local getRowAdapters = setmetatable({}, { __mode = "k" })
-local getColumnAdapters = setmetatable({}, { __mode = "k" })
+local getRowAdapters = lwtk.WeakKeysTable()
+local getColumnAdapters = lwtk.WeakKeysTable()
-local col4Caches = setmetatable({}, { __mode = "k" })
-local row4Caches = setmetatable({}, { __mode = "k" })
+local col4Caches = lwtk.WeakKeysTable()
+local row4Caches = lwtk.WeakKeysTable()
local ColumnAdapter = lwtk.newClass("lwtk.Matrix.ColumnAdapter")
local RowAdapter = lwtk.newClass("lwtk.Matrix.RowAdapter")
-------------------------------------------------------------------------------------------------
+ColumnAdapter:declare("visible")
+
+RowAdapter:declare("visible")
+
function ColumnAdapter:new()
self.visible = true
end
@@ -40,7 +44,10 @@ end
-------------------------------------------------------------------------------------------------
-function Matrix:new(initParams)
+Matrix:declare("columnCount",
+ "rowCount")
+
+function Matrix.override:new(initParams)
local columnCount = 0
local rows = {}
for i = 1, #initParams do
@@ -103,7 +110,7 @@ end
-------------------------------------------------------------------------------------------------
-function Matrix:getMeasures()
+function Matrix.implement:getMeasures()
local rowAdapters = getRowAdapters[self]
local columnAdapters = getColumnAdapters[self]
@@ -127,7 +134,7 @@ end
-------------------------------------------------------------------------------------------------
-function Matrix:onLayout(width, height, isLayoutTransition)
+function Matrix.implement:onLayout(width, height, isLayoutTransition)
if not isLayoutTransition then
local topMargin, rightMargin, bottomMargin, leftMargin = getOuterMargins(self)
local rowAdapters = getRowAdapters[self]
diff --git a/src/lwtk/Meta.lua b/src/lwtk/Meta.lua
new file mode 100644
index 0000000..d21173b
--- /dev/null
+++ b/src/lwtk/Meta.lua
@@ -0,0 +1,51 @@
+local lwtk = require("lwtk")
+
+--[[
+ Metatable for objects created by lwtk.newMeta().
+
+ See [lwtk,Meta Usage](../../Meta.md) for detailed documentation
+ and usage examples.
+]]
+local Meta = {}
+
+Meta.__name = "lwtk.Meta"
+
+function Meta:__tostring()
+ if self.__name then
+ return string.format("lwtk.Meta<%s>", self.__name)
+ else
+ return "lwtk.Meta"
+ end
+end
+
+local lua_version = _VERSION:match("[%d%.]*$")
+local isOldLua = (#lua_version == 3 and lua_version < "5.3")
+
+if isOldLua then
+ if pcall(function() string.format("%p", {}) end) then
+
+ Meta.fallbackToString = function(self)
+ local mt = getmetatable(self)
+ return string.format("%s: %p", mt.__name, self) -- luajit
+ end
+ else
+ Meta.fallbackToString = function(self)
+ local mt = debug.getmetatable(self)
+ local tmp = rawget(mt, "__tostring")
+ rawset(mt, "__tostring", nil)
+ local hash = tostring(self):match("([^ :]*)$")
+ rawset(mt, "__tostring", tmp)
+ return string.format("%s: %s", rawget(mt, "__name"), hash) -- lua5.1, lua5.2
+ end
+ end
+end
+
+function Meta:__call(...)
+ local obj = {}
+ setmetatable(obj, self)
+ local new = self.new
+ if new then new(obj, ...) end
+ return obj
+end
+
+return Meta
diff --git a/src/lwtk/Mixin.lua b/src/lwtk/Mixin.lua
index dd22971..d8cc862 100644
--- a/src/lwtk/Mixin.lua
+++ b/src/lwtk/Mixin.lua
@@ -1,6 +1,12 @@
-local Mixin = {
- __name = "lwtk.Mixin",
- __index = {}
-}
-return Mixin
+--[[
+ Metatable for objects created by lwtk.newMixin().
+
+ See [lwtk.Mixin Usage](../../Mixin.md) for detailed documentation
+ and usage examples.
+]]
+local Mixin = {}
+
+Mixin.__name = "lwtk.Mixin"
+
+return Mixin
diff --git a/src/lwtk/MouseDispatcher.lua b/src/lwtk/MouseDispatcher.lua
index e8f1f1a..d0f6b4b 100644
--- a/src/lwtk/MouseDispatcher.lua
+++ b/src/lwtk/MouseDispatcher.lua
@@ -2,16 +2,24 @@ local lwtk = require("lwtk")
local call = lwtk.call
-local MouseDispatcher = lwtk.newMixin("lwtk.MouseDispatcher", lwtk.Styleable.NO_STYLE_SELECTOR)
-
-function MouseDispatcher.initClass(MouseDispatcher, Super) -- luacheck: ignore 431/MouseDispatcher
-
- function MouseDispatcher:new(initParams)
- self.mouseChildButtons = {}
- Super.new(self, initParams)
+local MouseDispatcher = lwtk.newMixin("lwtk.MouseDispatcher", lwtk.Styleable.NO_STYLE_SELECTOR,
+
+ function(MouseDispatcher, Super)
+
+ MouseDispatcher:declare(
+ "mouseButtonChild",
+ "mouseChildButtons",
+ "mouseHoverChild",
+ "mouseX",
+ "mouseY"
+ )
+
+ function MouseDispatcher.override:new(initParams)
+ self.mouseChildButtons = {}
+ Super.new(self, initParams)
+ end
end
-
-end
+)
local function findChildAt(self, mx, my)
@@ -118,22 +126,22 @@ local function processMouseMove(self, entered, mx, my)
end
end
-function MouseDispatcher:_processMouseEnter(mx, my)
+function MouseDispatcher.override:_processMouseEnter(mx, my)
processMouseMove(self, true, mx, my)
end
-function MouseDispatcher:_processMouseMove(mouseEntered, mx, my)
+function MouseDispatcher.override:_processMouseMove(mouseEntered, mx, my)
processMouseMove(self, mouseEntered, mx, my)
end
-function MouseDispatcher:_processMouseScroll(dx, dy)
+function MouseDispatcher.override:_processMouseScroll(dx, dy)
local hChild = self.mouseHoverChild
if hChild and hChild ~= self then
hChild:_processMouseScroll(dx, dy)
end
end
-function MouseDispatcher:_processMouseLeave(mx, my)
+function MouseDispatcher.override:_processMouseLeave(mx, my)
self.mouseX = mx
self.mouseY = my
local bChild = self.mouseButtonChild
@@ -156,7 +164,7 @@ function MouseDispatcher:_processMouseLeave(mx, my)
end
end
-function MouseDispatcher:_processMouseDown(mx, my, button, modState)
+function MouseDispatcher.override:_processMouseDown(mx, my, button, modState)
self.mouseX = mx
self.mouseY = my
local bChild = self.mouseButtonChild
@@ -194,7 +202,7 @@ local function hasButtons(buttons)
end
end
-function MouseDispatcher:_processMouseUp(mouseEntered, mx, my, button, modState)
+function MouseDispatcher.override:_processMouseUp(mouseEntered, mx, my, button, modState)
self.mouseX = mx
self.mouseY = my
local bChild = self.mouseButtonChild
diff --git a/src/lwtk/Node.lua b/src/lwtk/Node.lua
new file mode 100644
index 0000000..d09e59b
--- /dev/null
+++ b/src/lwtk/Node.lua
@@ -0,0 +1,23 @@
+local lwtk = require"lwtk"
+
+
+local Node = lwtk.newMixin("lwtk.Node")
+
+Node:declare(
+ "_processMouseEnter",
+ "onMouseEnter",
+ "_processMouseLeave",
+ "onMouseLeave",
+ "_processMouseMove",
+ "onMouseMove",
+ "_processMouseScroll",
+ "onMouseScroll",
+ "_processMouseDown",
+ "onMouseDown",
+ "_processMouseUp",
+ "onMouseUp",
+ "onInputChanged"
+)
+
+
+return Node
diff --git a/src/lwtk/Object.lua b/src/lwtk/Object.lua
index 814ff04..8df3ca1 100644
--- a/src/lwtk/Object.lua
+++ b/src/lwtk/Object.lua
@@ -1,64 +1,191 @@
--
--- classic
---
--- Copyright (c) 2014, rxi
---
--- This module is free software; you can redistribute it and/or modify it under
--- the terms of the MIT license. See LICENSE for details.
---
--- copied from also https://github.com/rxi/classic
---
-
-local lua_version = _VERSION:match("[%d%.]*$")
-local isOldLua = (#lua_version == 3 and lua_version < "5.3")
+-- This code was inspired by:
+--
+-- classic
+--
+-- Copyright (c) 2014, rxi
+--
+-- This module is free software; you can redistribute it and/or modify it under
+-- the terms of the MIT license. See LICENSE for details.
+--
+-- see also https://github.com/rxi/classic
+--
-local upper = string.upper
-local sub = string.sub
+local rawset = rawset
+local rawget = rawget
+local getmetatable = getmetatable
+local upper = string.upper
+local sub = string.sub
local lwtk = require("lwtk")
local Class = lwtk.Class
+local type = lwtk.type
local getSuperClass = lwtk.get.superClass
+local getClass = lwtk.get.class
+local getObjectMeta = lwtk.get.objectMeta
+local getMixinBase = lwtk.get.mixinBase
+local indexMeta = {}
-local Object = {}
-Object.__index = Object
-Object.__name = "lwtk.Object"
-setmetatable(Object, Class)
+local createObjMetaNewIndex
-function Object:new()
+if _G.LWTK_DISABLE_CHECKS then
+ function createObjMetaNewIndex(newObjMeta, index)
+ end
+else
+ function createObjMetaNewIndex(newObjMeta, index)
+ function newObjMeta:__newindex(k, v)
+ if type(k) ~= "string" or index[k] ~= nil then
+ rawset(self, k, v)
+ else
+ lwtk.errorf("no object member %q declared in class %s", k, self:getClassPath())
+ end
+ end
+ end
+ function indexMeta:__index(k)
+ if type(k) == "string" then
+ lwtk.errorf("member %q not declared in class %s", k, getClass[getObjectMeta[self]]:getClassPath())
+ end
+ end
end
-local fallbackToString
-if isOldLua then
- fallbackToString = function(self)
- local mt = getmetatable(self)
- setmetatable(self, nil)
- local s = tostring(self)
- setmetatable(self, mt)
- return mt.__name..": "..s:match("([^ :]*)$")
+local function onext(obj, k)
+ local index = getObjectMeta[getmetatable(obj)].__index
+ while true do
+ k = next(index, k)
+ local rslt = (k ~= nil and obj[k])
+ if rslt ~= nil then
+ return k, rslt
+ end
end
end
-function Object.newSubClass(className, superClass)
+local function objPairs(obj)
+ return onext, obj, nil
+end
+
+local function createClass(newObjMeta, className, index, baseClass)
+ newObjMeta.__name = className
+ if newObjMeta.__tostring == nil then
+ newObjMeta.__tostring = lwtk.Meta.fallbackToString
+ end
+ createObjMetaNewIndex(newObjMeta, index)
local newClass = {}
- for k, v in pairs(superClass) do
- if k ~= "extra" then
- newClass[k] = v
+ newObjMeta.declared = {}
+ newObjMeta.__index = setmetatable(index, indexMeta)
+ newObjMeta.__metatable = newClass
+ newObjMeta.__pairs = objPairs
+
+ getObjectMeta[newClass] = newObjMeta
+ getObjectMeta[index] = newObjMeta
+ getSuperClass[newClass] = baseClass
+ getClass[newObjMeta] = newClass
+ setmetatable(newClass, Class)
+
+ return newClass
+end
+
+--[[
+ Superclass for all classes created by lwtk.newClass().
+]]
+local Object = createClass({}, "lwtk.Object", {})
+
+function Object.static.newSubClass(baseClass, className, ...)
+ assert(type(baseClass) == "lwtk.Class", "arg 1 must be of type lwtk.Class")
+ assert(type(className) == "string", "arg 2: exptected class name string")
+ local baseObjMeta = getObjectMeta[baseClass]
+ local newObjMeta = {}
+ for k, v in pairs(baseObjMeta) do
+ if type(k) == "string" and k ~= "extra" and k ~= "static" and k ~= "declared"
+ and k ~= "__index" and k ~= "override" and k ~= "__newindex" and k ~= "__name"
+ and k ~= "implement"
+ then
+ newObjMeta[k] = v
end
end
- newClass.__index = newClass
- newClass.__name = className
- if isOldLua and not newClass.__tostring then
- newClass.__tostring = fallbackToString
+ local index = {}
+ for k, v in pairs(baseObjMeta.__index) do
+ index[k] = v
end
- setmetatable(newClass, Class)
- getSuperClass[newClass] = superClass
+ local newClass = createClass(newObjMeta, className, index, baseClass)
return newClass
end
-
Object.isInstanceOf = lwtk.isInstanceOf
+local function getClassPath(self)
+ local s = getSuperClass[self]
+ local p = s and "("..getClassPath(s)..")" or ""
+ local mixinBase = getMixinBase[self]
+ if mixinBase then
+ return "#"..mixinBase.__name..p
+ else
+ return self.__name..p
+ end
+end
+
+function Object:getClassPath()
+ local mt = getmetatable(self)
+ if mt == Class then
+ return getClassPath(self)
+ else
+ return getClassPath(mt)
+ end
+end
+
+--[[
+ Returns the superclass of this object.
+
+ This method can also be called on a class. In this
+ case the superclass of the given class is returned.
+]]
+function Object:getSuperClass()
+ local mt = getmetatable(self)
+ if mt == Class then
+ return getSuperClass[self]
+ else
+ return getSuperClass[mt]
+ end
+end
+
+function Object:getMember(name)
+ local mt = getmetatable(self)
+ if mt == Class then
+ local objMeta = getObjectMeta[self]
+ return rawget(objMeta.__index, name)
+ else
+ local m = rawget(self, name)
+ if m == nil then
+ m = rawget(getmetatable(self).__index, name)
+ end
+ return m
+ end
+end
+
+local function getReverseClassPath(self)
+ local s = getSuperClass[self]
+ local p = s and getReverseClassPath(s).."/" or "/"
+ local mixinBase = getMixinBase[self]
+ if mixinBase then
+ return p.."#"..mixinBase.__name
+ else
+ return p..self.__name
+ end
+end
+
+function Object:getReverseClassPath()
+ local mt = getmetatable(self)
+ if mt == Class then
+ return getReverseClassPath(self)
+ else
+ return getReverseClassPath(mt)
+ end
+end
+
+function Object:getClass()
+ return getmetatable(self)
+end
+
function Object:setAttributes(attr)
if attr ~= nil then
assert(type(attr) == "table", "table expected")
@@ -72,4 +199,45 @@ function Object:setAttributes(attr)
end
end
+function Object.static:getMixinBase()
+ return getMixinBase[self]
+end
+
+if _G.LWTK_DISABLE_CHECKS then
+ function Object.static:declare(...)
+ for i = 1, select("#", ...) do
+ local var = select(i, ...)
+ self[var] = false
+ end
+ end
+else
+ function Object.static:declare(...)
+ for i = 1, select("#", ...) do
+ local var = select(i, ...)
+ if var:match("^[a-zA-Z_][a-zA-Z_0-9]*$") then
+ local objMeta = getObjectMeta[self]
+ if objMeta[var] == nil then
+ self[var] = false
+ else
+ local c = self
+ local m = objMeta
+ while true do
+ if m.declared[var] then
+ if c[var] ~= false then
+ lwtk.errorf("member %q from class %q is already defined in superclass %q in class path %s", var, objMeta.__name, m.__name, self:getClassPath())
+ else
+ lwtk.errorf("member %q from class %q is already declared in superclass %q in class path %s", var, objMeta.__name, m.__name, self:getClassPath())
+ end
+ end
+ c = getSuperClass[c]
+ m = getObjectMeta[c]
+ end
+ end
+ else
+ lwtk.errorf("invalid member name %q", var)
+ end
+ end
+ end
+end
+
return Object
diff --git a/src/lwtk/PushButton.lua b/src/lwtk/PushButton.lua
index 66b91ee..4de805c 100644
--- a/src/lwtk/PushButton.lua
+++ b/src/lwtk/PushButton.lua
@@ -4,10 +4,19 @@ local TextFragment = lwtk.TextFragment
local Super = lwtk.Focusable(lwtk.Button)
local PushButton = lwtk.newClass("lwtk.PushButton", Super)
-PushButton.getMeasures = lwtk.TextLabel.getMeasures
+PushButton.implement.getMeasures = lwtk.TextLabel.getMeasures
PushButton.setDefault = lwtk.Focusable.extra.setDefault
-function PushButton:new(initParams)
+PushButton:declare(
+ "textFragment",
+ "text",
+ "onClicked",
+ "mousePressed",
+ "mouseEntered",
+ "_mouseDownTime"
+)
+
+function PushButton.override:new(initParams)
Super.new(self)
self.textFragment = self:addChild(TextFragment { considerHotkey = true })
self:setInitParams(initParams)
@@ -44,19 +53,19 @@ function PushButton:setText(text)
self:triggerLayout()
end
end
-function PushButton:onMouseEnter(x, y)
+function PushButton.implement:onMouseEnter(x, y)
self.mouseEntered = true
if not self.disabled then
self:setState("hover", true)
end
end
-function PushButton:onMouseLeave(x, y)
+function PushButton.implement:onMouseLeave(x, y)
self.mouseEntered = false
if not self.disabled then
self:setState("hover", false)
end
end
-function PushButton:onMouseDown(x, y, button, modState)
+function PushButton.implement:onMouseDown(x, y, button, modState)
if button == 1 and not self.disabled then
self.mousePressed = true
self:setState("pressed", true)
@@ -72,7 +81,7 @@ local function onMouseUp2(self)
end
end
-function PushButton:onMouseUp(x, y, button, modState)
+function PushButton.implement:onMouseUp(x, y, button, modState)
if button == 1 and not self.disabled and self.mousePressed then
local simulateSeconds = self:getStyleParam("SimulateButtonClickSeconds") or 0.1
local mouseDownSeconds = self:getCurrentTime() - self._mouseDownTime
@@ -106,7 +115,7 @@ local function simulateButtonClick1(self)
simulateButtonClick2, self)
end
-function PushButton:onHotkeyDown()
+function PushButton.implement:onHotkeyDown()
simulateButtonClick1(self)
end
@@ -115,14 +124,14 @@ function PushButton:onActionFocusedButtonClick()
end
-function PushButton:onHotkeyEnabled(hotkey)
+function PushButton.override:onHotkeyEnabled(hotkey)
Super.onHotkeyEnabled(self, hotkey)
if self.textFragment.hotkey == hotkey then
self.textFragment:setShowHotkey(true)
end
end
-function PushButton:onHotkeyDisabled(hotkey)
+function PushButton.override:onHotkeyDisabled(hotkey)
Super.onHotkeyDisabled(self, hotkey)
if self.textFragment.hotkey == hotkey then
self.textFragment:setShowHotkey(false)
@@ -130,7 +139,7 @@ function PushButton:onHotkeyDisabled(hotkey)
end
-function PushButton:onLayout(width, height)
+function PushButton.override:onLayout(width, height)
Super.onLayout(self, width, height)
local iw, ih = self.textFragment:getSize()
local tw, th, ascent = self.textFragment:getTextMeasures()
diff --git a/src/lwtk/Rect.lua b/src/lwtk/Rect.lua
index c34fe4f..d336fe1 100644
--- a/src/lwtk/Rect.lua
+++ b/src/lwtk/Rect.lua
@@ -26,7 +26,7 @@ function Rect:toXYWH()
return self.x, self.y, self.width, self.height
end
-local function areRectsIntersected(x1, y1, w1, h1, x2, y2, w2, h2)
+function Rect.areRectsIntersected(x1, y1, w1, h1, x2, y2, w2, h2)
if x1 >= x2 + w2 or x1 + w1 <= x2 then
return false; -- one rectangle is on left side of other
end
@@ -37,14 +37,14 @@ local function areRectsIntersected(x1, y1, w1, h1, x2, y2, w2, h2)
end
-Rect.areRectsIntersected = areRectsIntersected
+local areRectsIntersected = Rect.areRectsIntersected
function Rect:intersects(x, y, w, h)
return areRectsIntersected(self.x, self.y, self.width, self.height,
x, y, w, h)
end
-local function doesRectContain(x1, y1, w1, h1, x2, y2, w2, h2)
+function Rect.doesRectContain(x1, y1, w1, h1, x2, y2, w2, h2)
local myX0, myY0 = x1, y1
local myX1, myY1 = myX0 + w1, myY0 + h1
@@ -58,7 +58,7 @@ local function doesRectContain(x1, y1, w1, h1, x2, y2, w2, h2)
and myY0 < otherY1 and otherY1 <= myY1
end
-Rect.doesRectContain = doesRectContain
+local doesRectContain = Rect.doesRectContain
function Rect:contains(x, y, w, h)
return doesRectContain(self.x, self.y, self.width, self.height,
@@ -76,7 +76,7 @@ function Rect:__tostring()
return format("lwtk.Rect(%d,%d,%d,%d)", self:toXYWH())
end
-local function intersectRects(x1, y1, w1, h1, x2, y2, w2, h2)
+function Rect.intersectRects(x1, y1, w1, h1, x2, y2, w2, h2)
if areRectsIntersected(x1, y1, w1, h1, x2, y2, w2, h2) then
local x = (x1 >= x2) and x1 or x2
local y = (y1 >= y2) and y1 or y2
@@ -88,6 +88,5 @@ local function intersectRects(x1, y1, w1, h1, x2, y2, w2, h2)
end
end
-Rect.intersectRects = intersectRects
return Rect
diff --git a/src/lwtk/Row.lua b/src/lwtk/Row.lua
index 74beab5..60b8154 100644
--- a/src/lwtk/Row.lua
+++ b/src/lwtk/Row.lua
@@ -5,7 +5,17 @@ local Row = lwtk.newClass("lwtk.Row", Super)
-------------------------------------------------------------------------------------------------
-lwtk.internal.ColumnImpl.implementColumn(Row, true)
+local impl = lwtk.internal.ColumnImpl.implementColumn(true)
+
+--[[
+ @function(self)
+]]
+Row.implement.getMeasures = impl.getMeasures
+
+--[[
+ @function(self, width, height, isLayoutTransition)
+]]
+Row.implement.onLayout = impl.onLayout
-------------------------------------------------------------------------------------------------
diff --git a/src/lwtk/Space.lua b/src/lwtk/Space.lua
index c3d17bd..ae40033 100644
--- a/src/lwtk/Space.lua
+++ b/src/lwtk/Space.lua
@@ -1,5 +1,7 @@
local lwtk = require("lwtk")
+local rawget = rawget
+
local getMeasures = lwtk.layout.getMeasures
local getOuterMargins = lwtk.layout.getOuterMargins
local setOuterMargins = lwtk.layout.setOuterMargins
@@ -7,19 +9,23 @@ local setOuterMargins = lwtk.layout.setOuterMargins
local Super = lwtk.Group
local Space = lwtk.newClass("lwtk.Space", Super)
+Space:declare(
+ "unlimited"
+)
+
function Space:setUnlimited(unlimited)
self.unlimited = unlimited
end
-function Space:addChild(child)
- if self[1] then
+function Space.override:addChild(child)
+ if rawget(self, 1) then
lwtk.errorf("object of type %s can only have one child", lwtk.type(self))
end
return Super.addChild(self, child)
end
-function Space:getMeasures()
- local child = self[1]
+function Space.implement:getMeasures()
+ local child = rawget(self, 1)
local unlimited = self.unlimited and -1 or -2
if child then
local minW, minH, bestW, bestH, _, _,
@@ -31,8 +37,8 @@ function Space:getMeasures()
end
end
-function Space:onLayout(w, h)
- local child = self[1]
+function Space.implement:onLayout(w, h)
+ local child = rawget(self, 1)
if child then
local tM, rM, bM, lM = getOuterMargins(self)
if child.getMeasures then
diff --git a/src/lwtk/Square.lua b/src/lwtk/Square.lua
index 8500be7..7c8c2f7 100644
--- a/src/lwtk/Square.lua
+++ b/src/lwtk/Square.lua
@@ -6,14 +6,14 @@ local setOuterMargins = lwtk.layout.setOuterMargins
local Super = lwtk.Group
local Square = lwtk.newClass("lwtk.Square", Super)
-function Square:addChild(child)
+function Square.override:addChild(child)
if self[1] then
lwtk.errorf("object of type %s can only have one child", lwtk.type(self))
end
return Super.addChild(self, child)
end
-function Square:getMeasures()
+function Square.implement:getMeasures()
local child = self[1]
local topMargin, rightMargin, bottomMargin, leftMargin
if child then
@@ -34,7 +34,7 @@ function Square:getMeasures()
return 0, 0, 0, 0, 0, 0
end
-function Square:onLayout(w, h)
+function Square.implement:onLayout(w, h)
local child = self[1]
if child then
local tM, rM, bM, lM = getOuterMargins(self)
diff --git a/src/lwtk/Style.lua b/src/lwtk/Style.lua
index 0683d7c..11980f6 100644
--- a/src/lwtk/Style.lua
+++ b/src/lwtk/Style.lua
@@ -18,11 +18,25 @@ local childStyles = WeakKeysTable()
local toTypePattern = TypeRule.toPattern
local toStylePattern = StyleRule.toPattern
+Style:declare(
+ "typeList",
+ "myScaleFactor",
+ "ruleList",
+ "cache",
+ "animatable",
+ "scalable",
+ "scaleFactor",
+ "localParams",
+ "parent"
+)
+
function Style:new(ruleList)
self:setRules(ruleList)
end
-local function clearCache(self)
+local clearCache
+
+function Style:clearCache()
self.cache = {}
self.animatable = {}
self.scalable = {}
@@ -35,7 +49,7 @@ local function clearCache(self)
end
end
-Style.clearCache = clearCache
+clearCache = Style.clearCache
function Style:setScaleFactor(scaleFactor)
self.myScaleFactor = scaleFactor
@@ -195,7 +209,7 @@ function Style:isScalable(parName)
end
end
-local function _getStyleParam3(self, selector, parName, classSelectorPath, stateSelectorPath, localStyle, ctxRules, localParams)
+local function _getStyleParam3(self, selector, parName, classSelectorPath, stateSelectorPath, localStyle, ctxRules, localParams, extraParentStyle)
local typeRule
local context
local function evalRule(rule)
@@ -243,7 +257,7 @@ local function _getStyleParam3(self, selector, parName, classSelectorPath, state
return rslt, typeRule, (context and context.localInvolved)
end
end
- local parent = self.parent
+ local parent = extraParentStyle or self.parent
if parent then
return _getStyleParam3(parent, selector, parName, classSelectorPath, stateSelectorPath, localStyle, ctxRules, localParams)
end
@@ -254,7 +268,7 @@ function Style:_getStyleParam2(parName, classSelectorPath, stateSelectorPath, ct
return _getStyleParam3(self, selector, parName, classSelectorPath, stateSelectorPath, self, ctxRules)
end
-function Style:_getStyleParam(parName, classSelectorPath, stateSelectorPath, considerLocal)
+function Style:_getStyleParam(parName, classSelectorPath, stateSelectorPath, considerLocal, extraParentStyle)
local cache = self.cache
local lowerParName = lower(parName)
local selector = lowerParName.."@"..classSelectorPath..":"..lower(stateSelectorPath)
@@ -276,10 +290,14 @@ function Style:_getStyleParam(parName, classSelectorPath, stateSelectorPath, con
if not typeRule then
errorf("Cannot deduce type for style parameter name %q", parName)
end
+ if type(rslt) == "function" then
+ local context = StyleRuleContext(nil, extraParentStyle or self.parent, classSelectorPath, stateSelectorPath)
+ rslt = rslt(context)
+ end
end
end
if not rslt then
- rslt, typeRule, localInvolved = _getStyleParam3(self, selector, parName, classSelectorPath, stateSelectorPath, self, nil, localParams)
+ rslt, typeRule, localInvolved = _getStyleParam3(self, selector, parName, classSelectorPath, stateSelectorPath, self, nil, localParams, extraParentStyle)
end
if rslt then
diff --git a/src/lwtk/StyleTypeAttributes.lua b/src/lwtk/StyleTypeAttributes.lua
index 5d74046..dbb66fb 100644
--- a/src/lwtk/StyleTypeAttributes.lua
+++ b/src/lwtk/StyleTypeAttributes.lua
@@ -1,14 +1,8 @@
local StyleTypeAttributes = {}
-local attributeNames = { "SCALABLE", "ANIMATABLE" }
-StyleTypeAttributes.toAttrName = {}
-
-for _, n in ipairs(attributeNames) do
- local a = {}
- StyleTypeAttributes[n] = a
- StyleTypeAttributes.toAttrName[a] = n
-end
+StyleTypeAttributes.SCALABLE = "SCALABLE"
+StyleTypeAttributes.ANIMATABLE = "ANIMATABLE"
return StyleTypeAttributes
diff --git a/src/lwtk/Styleable.lua b/src/lwtk/Styleable.lua
index b8d8922..d11247d 100644
--- a/src/lwtk/Styleable.lua
+++ b/src/lwtk/Styleable.lua
@@ -11,45 +11,52 @@ local getStylePath = lwtk.get.stylePath
local getSuperClass = lwtk.get.superClass
local isInstanceOf = lwtk.isInstanceOf
-local getStateStylePath = setmetatable({}, { __mode = "k" })
-
-local Styleable = lwtk.newMixin("lwtk.Styleable")
+local getStateStylePath = lwtk.WeakKeysTable()
local NO_STYLE_SELECTOR = {}
-Styleable.NO_STYLE_SELECTOR = NO_STYLE_SELECTOR
local function addToStyleSelectorClassPath(path, name)
return (path or "").."<"..lower(name)..">"
end
-function Styleable.initClass(Styleable, Super) -- luacheck: ignore 431/Styleable
-
- function Styleable.newSubClass(className, baseClass, ...)
- local newClass = Super.newSubClass(className, baseClass, ...)
- local path = getStylePath[getSuperClass[newClass]]
- local addToStyleSelector = true
- for i = 1, select("#", ...) do
- if select(i, ...) == NO_STYLE_SELECTOR then
- addToStyleSelector = false
- break
+local Styleable = lwtk.newMixin("lwtk.Styleable",
+
+ function(Styleable, Super)
+
+ Styleable:declare(
+ "_hasOwnStyle",
+ "state"
+ )
+
+ function Styleable.static.newSubClass(baseClass, className, ...)
+ local newClass = Super.newSubClass(baseClass, className, ...)
+ local path = getStylePath[getSuperClass[newClass]]
+ local addToStyleSelector = true
+ for i = 1, select("#", ...) do
+ if select(i, ...) == NO_STYLE_SELECTOR then
+ addToStyleSelector = false
+ break
+ end
end
+ if addToStyleSelector then
+ path = addToStyleSelectorClassPath(path, className:match("^[^(]*"), ...)
+ end
+ getStylePath[newClass] = path
+ return newClass
end
- if addToStyleSelector then
- path = addToStyleSelectorClassPath(path, className, ...)
+
+ function Styleable.override:new(initParams)
+ local stylePath = getStylePath[self:getClass()]
+ if stylePath then
+ getStylePath[self] = stylePath
+ self.state = {}
+ end
+ Super.new(self, initParams)
end
- getStylePath[newClass] = path
- return newClass
end
+)
- function Styleable:new(initParams)
- local stylePath = getStylePath[getmetatable(self)]
- if stylePath then
- getStylePath[self] = stylePath
- self.state = {}
- end
- Super.new(self, initParams)
- end
-end
+Styleable.NO_STYLE_SELECTOR = NO_STYLE_SELECTOR
function Styleable:setState(name, flag)
flag = flag and true or false
@@ -58,7 +65,7 @@ function Styleable:setState(name, flag)
getStateStylePath[state] = false
end
-function Styleable:_setStyleFromParent(parentStyle)
+function Styleable.implement:_setStyleFromParent(parentStyle)
self:triggerLayout()
local style
if self._hasOwnStyle then
@@ -68,8 +75,8 @@ function Styleable:_setStyleFromParent(parentStyle)
style = parentStyle
getStyle[self] = style
end
- for _, child in ipairs(self) do
- call("_setStyleFromParent", child, style)
+ for i = 1, #self do
+ call("_setStyleFromParent", self[i], style)
end
end
@@ -86,7 +93,8 @@ function Styleable:setStyle(style)
style:_setParentStyle(getStyle[self])
end
getStyle[self] = style
- for _, child in ipairs(self) do
+ for i = 1, #self do
+ local child = rawget(self, i)
call("_setStyleFromParent", child, style)
end
end
@@ -132,7 +140,7 @@ end
local getStateString = Styleable.getStateString
-local function _getStyleParam(self, style, paramName)
+local function _getStyleParam2(self, paramName, style, extraParentStyle)
local stylePath = getStylePath[self]
if not stylePath then
local p = getParent[self]
@@ -149,22 +157,29 @@ local function _getStyleParam(self, style, paramName)
end
assert(stylePath, "Widget not connected to parent")
local statePath = getStateString(self)
- local rslt = style:_getStyleParam(paramName, stylePath, statePath, self._hasOwnStyle)
+ local rslt = style:_getStyleParam(paramName, stylePath, statePath, self._hasOwnStyle, extraParentStyle)
if not rslt and paramName:match("^.*Opacity$") then
return 1
else
return rslt
end
end
-Styleable._getStyleParam = _getStyleParam
+function Styleable:_getStyleParam(style, paramName)
+ local myStyle = getStyle[self]
+ if myStyle then
+ return _getStyleParam2(self, paramName, myStyle, style)
+ else
+ return _getStyleParam2(self, paramName, style)
+ end
+end
-function Styleable:getStyleParam(paramName)
+function Styleable.override:getStyleParam(paramName)
if not self.visible and paramName == "Opacity" then
return 0
end
local style = getStyle[self]
if style then
- return _getStyleParam(self, style, paramName)
+ return _getStyleParam2(self, paramName, style)
end
end
diff --git a/src/lwtk/TextCursor.lua b/src/lwtk/TextCursor.lua
index fccb6d1..c7288c8 100644
--- a/src/lwtk/TextCursor.lua
+++ b/src/lwtk/TextCursor.lua
@@ -4,7 +4,7 @@ local Color = lwtk.Color
local Super = lwtk.Component
local TextCursor = lwtk.newClass("lwtk.TextCursor", Super)
-function TextCursor:onDraw(ctx)
+function TextCursor.implement:onDraw(ctx)
local color = self:getStyleParam("CursorColor") or Color("000000")
ctx:fillRect(color, 0, 0, self.w, self.h)
end
diff --git a/src/lwtk/TextFragment.lua b/src/lwtk/TextFragment.lua
index 4987dc9..edd1def 100644
--- a/src/lwtk/TextFragment.lua
+++ b/src/lwtk/TextFragment.lua
@@ -4,10 +4,24 @@ local utf8 = lwtk.utf8
local Super = lwtk.Component
local TextFragment = lwtk.newClass("lwtk.TextFragment", Super)
-function TextFragment:new(initParams)
- self.tx = false
- self.ty = false
- self.label = ""
+TextFragment:declare(
+ "tx",
+ "ty",
+ "label",
+ "labelLeft",
+ "labelKey",
+ "labelRight",
+ "text",
+ "textWidth",
+ "considerHotkey",
+ "hotkey",
+ "fontInfo",
+ "textSize",
+ "fontFamily",
+ "showHotKey"
+)
+
+function TextFragment.override:new(initParams)
Super.new(self, initParams)
end
@@ -78,13 +92,20 @@ local function getFontInfo(self)
end
end
-TextFragment.getFontInfo = getFontInfo
+function TextFragment.override:getFontInfo(family, slant, weight, size)
+ if family then
+ return Super_getFontInfo(self, family, slant, weight, size)
+ else
+ return getFontInfo(self)
+ end
+end
function TextFragment:getTextMeasures()
local fontInfo, changed = getFontInfo(self)
local textWidth = self.textWidth
if changed or not textWidth then
- textWidth = fontInfo:getTextWidth(self.label)
+ local label = self.label
+ textWidth = label and fontInfo:getTextWidth(label) or 0
self.textWidth = textWidth
end
return textWidth, fontInfo.height, fontInfo.ascent
@@ -98,7 +119,7 @@ function TextFragment:setTextPos(tx, ty)
end
end
-function TextFragment:onDraw(ctx, ...)
+function TextFragment.implement:onDraw(ctx, ...)
local label = self.label
if label then
local fontInfo = getFontInfo(self)
@@ -118,7 +139,7 @@ function TextFragment:onDraw(ctx, ...)
if self.hotkey and self.showHotKey then
local x1 = tx + fontInfo:getTextWidth(self.labelLeft)
local x2 = x1 + fontInfo:getTextWidth(self.labelKey)
- local y1 = ty + math.floor(fontInfo.descent / 2 + 0.5) - 0.5
+ local y1 = ty + 1.5
ctx:setLineWidth(1)
ctx:drawLine(x1, y1, x2, y1)
end
diff --git a/src/lwtk/TextInput.lua b/src/lwtk/TextInput.lua
index 59bee83..30df886 100644
--- a/src/lwtk/TextInput.lua
+++ b/src/lwtk/TextInput.lua
@@ -15,12 +15,24 @@ local getApp = lwtk.get.app
local Super = lwtk.Focusable(lwtk.Control(lwtk.Compound(lwtk.Widget)))
local TextInput = lwtk.newClass("lwtk.TextInput", Super)
-TextInput.getMeasures = lwtk.TextLabel.getMeasures
-TextInput._isInput = true
+TextInput:declare(
+ "initActions",
+ "inner",
+ "cursor",
+ "textFragment",
+ "cursorPos",
+ "tx",
+ "text",
+ "filterInput"
+)
-function TextInput:new(initParams)
+TextInput.implement.getMeasures = lwtk.TextLabel.getMeasures
+
+TextInput.implement._isInput = true
+
+function TextInput.override:new(initParams)
Super.new(self)
- self.initActions = extract(initParams, "initActions")
+ self.initActions = extract(initParams, "initActions")
self.inner = self:addChild(InnerCompound())
self.cursor = self.inner:addChild(TextCursor())
self.textFragment = self.inner:addChild(TextFragment())
@@ -32,7 +44,7 @@ function TextInput:new(initParams)
end
end
-function TextInput:onRealize()
+function TextInput.implement:onRealize()
if Super.onRealize then
Super.onRealize(self)
end
@@ -116,12 +128,12 @@ function TextInput:setDefault(buttonId)
end
end
-function TextInput:onLayout(width, height)
+function TextInput.override:onLayout(width, height)
Super.onLayout(self, width, height)
innerLayout(self)
end
-function TextInput:onFocusIn()
+function TextInput.implement:onFocusIn()
self:setState("focused", true)
if self.default then
local focusHandler = getFocusHandler[self]
@@ -130,7 +142,7 @@ function TextInput:onFocusIn()
end
end
end
-function TextInput:onFocusOut()
+function TextInput.implement:onFocusOut()
self:setState("focused", false)
if self.default then
local focusHandler = getFocusHandler[self]
@@ -140,7 +152,7 @@ function TextInput:onFocusOut()
end
end
-function TextInput:onMouseDown(mx, my, button, modState)
+function TextInput.implement:onMouseDown(mx, my, button, modState)
if button == 1 then
self:setFocus(true)
local fontInfo = self.textFragment:getFontInfo()
@@ -255,7 +267,7 @@ function TextInput:setFilterInput(filterInput)
self.filterInput = filterInput
end
-function TextInput:onKeyDown(keyName, keyState, keyText)
+function TextInput.implement:onKeyDown(keyName, keyState, keyText)
if keyText and keyText ~= "" then
local filterInput = self.filterInput
if filterInput then
diff --git a/src/lwtk/TextLabel.lua b/src/lwtk/TextLabel.lua
index 69985f1..491fe6f 100644
--- a/src/lwtk/TextLabel.lua
+++ b/src/lwtk/TextLabel.lua
@@ -6,7 +6,13 @@ local TextFragment = lwtk.TextFragment
local Super = lwtk.Button
local TextLabel = lwtk.newClass("lwtk.TextLabel", Super)
-function TextLabel:new(initParams)
+TextLabel:declare(
+ "textFragment",
+ "text",
+ "input"
+)
+
+function TextLabel.override:new(initParams)
Super.new(self)
self.textFragment = self:addChild(TextFragment { considerHotkey = true })
self:setInitParams(initParams)
@@ -23,14 +29,14 @@ function TextLabel:setInput(inputId)
self.input = inputId
end
-function TextLabel:onHotkeyEnabled(hotkey)
+function TextLabel.override:onHotkeyEnabled(hotkey)
Super.onHotkeyEnabled(self, hotkey)
if self.textFragment.hotkey == hotkey then
self.textFragment:setShowHotkey(true)
end
end
-function TextLabel:onHotkeyDisabled(hotkey)
+function TextLabel.override:onHotkeyDisabled(hotkey)
Super.onHotkeyDisabled(self, hotkey)
if self.textFragment.hotkey == hotkey then
self.textFragment:setShowHotkey(false)
@@ -46,13 +52,13 @@ function TextLabel:onHotkeyDown()
end
end
end
-function TextLabel:onMouseDown(x, y, button, modState)
+function TextLabel.implement:onMouseDown(x, y, button, modState)
if button == 1 then
self:onHotkeyDown()
end
end
-function TextLabel:getMeasures()
+function TextLabel.implement:getMeasures()
local addW = (self:getStyleParam("LeftPadding") or 0)
+ (self:getStyleParam("RightPadding") or 0)
+ 2 * (self:getStyleParam("BorderPadding") or 0)
@@ -123,7 +129,7 @@ function TextLabel:getMeasures()
end
-function TextLabel:onLayout(width, height)
+function TextLabel.override:onLayout(width, height)
Super.onLayout(self, width, height)
local iw, ih = self.textFragment:getSize()
local tw, th, ascent = self.textFragment:getTextMeasures()
diff --git a/src/lwtk/Timer.lua b/src/lwtk/Timer.lua
index d66097a..a3a497f 100644
--- a/src/lwtk/Timer.lua
+++ b/src/lwtk/Timer.lua
@@ -2,11 +2,16 @@ local lwtk = require"lwtk"
local Timer = lwtk.newClass("lwtk.Timer")
+Timer:declare(
+ "n",
+ "func",
+ "time"
+)
function Timer:new(func, ...)
local n = select("#", ...)
for i = 1, n do
- self[i] = select(i, ...)
+ rawset(self, i, select(i, ...))
end
self.n = n
self.func = func
diff --git a/src/lwtk/ViewSwitcher.lua b/src/lwtk/ViewSwitcher.lua
index ed6daba..2aa273f 100644
--- a/src/lwtk/ViewSwitcher.lua
+++ b/src/lwtk/ViewSwitcher.lua
@@ -7,7 +7,7 @@ local setOuterMargins = lwtk.layout.setOuterMargins
local Super = lwtk.Colored(lwtk.Group)
local ViewSwitcher = lwtk.newClass("lwtk.ViewSwitcher", Super)
-function ViewSwitcher:onLayout(w, h)
+function ViewSwitcher.implement:onLayout(w, h)
local topMargin, rightMargin, bottomMargin, leftMargin = getOuterMargins(self)
for i = 1, #self do
local child = self[i]
@@ -16,7 +16,7 @@ function ViewSwitcher:onLayout(w, h)
end
end
-function ViewSwitcher:getMeasures()
+function ViewSwitcher.implement:getMeasures()
local minW, minH, bestW, bestH, maxW, maxH,
childTop, childRight, childBottom, childLeft
@@ -62,7 +62,7 @@ function ViewSwitcher:getMeasures()
end
end
-function ViewSwitcher:addChild(child)
+function ViewSwitcher.override:addChild(child)
local childVisible = child.visible
if childVisible then
for i = 1, #self do
diff --git a/src/lwtk/WeakKeysTable.lua b/src/lwtk/WeakKeysTable.lua
index 04d7fb4..97cc5d6 100644
--- a/src/lwtk/WeakKeysTable.lua
+++ b/src/lwtk/WeakKeysTable.lua
@@ -1,8 +1,7 @@
-local weakKeysMeta = { __name = "lwtk.WeakKeysTable",
- __mode = "k" }
+local lwtk = require("lwtk")
-local function WeakKeysTable()
- return setmetatable({}, weakKeysMeta)
-end
+local WeakKeysTable = lwtk.newMeta("lwtk.WeakKeysTable")
+
+WeakKeysTable.__mode = "k"
return WeakKeysTable
diff --git a/src/lwtk/Widget.lua b/src/lwtk/Widget.lua
index afac519..ec5946d 100644
--- a/src/lwtk/Widget.lua
+++ b/src/lwtk/Widget.lua
@@ -7,17 +7,18 @@ local Callback = lwtk.Callback
local Super = lwtk.Animatable(lwtk.Component)
local Widget = lwtk.newClass("lwtk.Widget", Super)
-function Widget:new(initParams)
+Widget:declare(
+ "default",
+ "_isInput"
+)
+
+function Widget.override:new(initParams)
Super.new(self)
if initParams then
self:setInitParams(initParams)
end
end
-function Widget:setInitParams(initParams)
- Super.setInitParams(self, initParams)
-end
-
function Widget:setOnInputChanged(onInputChanged)
self.onInputChanged = onInputChanged
end
@@ -37,7 +38,7 @@ function Widget:notifyInputChanged()
until w == nil
end
-function Widget:_setApp(app)
+function Widget.override:_setApp(app)
if self._hasOwnStyle then
local ownStyle = getStyle[self]
if not ownStyle.parent then
@@ -53,7 +54,7 @@ function Widget:_setApp(app)
end
end
-function Widget:_setParent(parent)
+function Widget.override:_setParent(parent)
local pstyle = getStyle[parent]
if pstyle then
self:_setStyleFromParent(pstyle)
diff --git a/src/lwtk/WidgetWrapper.lua b/src/lwtk/WidgetWrapper.lua
deleted file mode 100644
index f73d0b4..0000000
--- a/src/lwtk/WidgetWrapper.lua
+++ /dev/null
@@ -1,127 +0,0 @@
-local lwtk = require"lwtk"
-
-local upper = string.upper
-local lower = string.lower
-local sub = string.sub
-local format = string.format
-local match = string.match
-
-local newClass = lwtk.newClass
-local getWrapper = lwtk.get.wrapper
-local getWrappedChild = lwtk.get.wrappedChild
-local getWrappingParent = lwtk.get.wrappingParent
-local getStylePath = lwtk.get.stylePath
-
-local function cloneClass(class, selectorFrag)
- local cloned = {}
- for k, v in pairs(class) do
- cloned[k] = v
- end
- cloned.__index = cloned
- local selector = getStylePath[class]
- if selector and selectorFrag then
- getStylePath[cloned] = selector.."<"..lower(selectorFrag)..">"
- end
- setmetatable(cloned, getmetatable(class))
- return cloned
-end
-
-local function shortName(className)
- return match(className, "^.*%.([^.]*)$") or className
-end
-
-local function newWrapperClass(className, WrappedChildClass, WrappingParentClass, parentAttributes, parentMethods)
-
-
- local shortWrapperName = shortName(className)
- local shortWrappedName = shortName(WrappedChildClass.__name)
- local selectorFrag = format("%s(%s)", shortWrapperName, shortWrappedName)
-
- local WrapperClass = newClass(format("%s(%s)", className, tostring(WrappedChildClass)))
-
- WrappedChildClass = cloneClass(WrappedChildClass, selectorFrag)
- WrappingParentClass = cloneClass(WrappingParentClass, selectorFrag)
-
- for k, v in pairs(WrappedChildClass) do
- if type(v) == "function" and type(k) == "string" and not k:match("^__") then
- WrapperClass[k] = function(self, ...)
- return v(getWrappedChild[self], ...)
- end
- end
- end
-
- for _, a in ipairs(parentAttributes) do
- local setterName = "set"..upper(sub(a, 1, 1))..sub(a, 2)
- local getterName = "get"..upper(sub(a, 1, 1))..sub(a, 2)
- local setter = WrappingParentClass[setterName]
- local getter = WrappingParentClass[getterName]
- if setter then
- WrapperClass[setterName] = function(self, ...)
- return setter(getWrappingParent[self], ...)
- end
- end
- if getter then
- WrapperClass[getterName] = function(self, ...)
- return getter(getWrappingParent[self], ...)
- end
- end
- end
- for _, methodName in ipairs(parentMethods) do
- local method = WrappingParentClass[methodName]
- if method then
- WrapperClass[methodName] = function(self, ...)
- return method(getWrappingParent[self], ...)
- end
- end
- end
-
- function WrapperClass:new(initParams)
- local parentParams
- if initParams then
- self.id = initParams.id
- parentParams = {}
- for _, a in ipairs(parentAttributes) do
- local p = initParams[a]
- if p then
- parentParams[a] = p
- initParams[a] = nil
- end
- end
- end
- local wrappingParent = WrappingParentClass(parentParams)
- local wrappedChild = WrappedChildClass(initParams)
- wrappingParent:addChild(wrappedChild)
- getWrappingParent[self] = wrappingParent
- getWrappedChild[self] = wrappedChild
- getWrapper[wrappedChild] = self
- end
-
- function WrapperClass:getWrappingParent()
- return getWrappingParent[self]
- end
- function WrapperClass:getWrappedChild()
- return getWrappedChild[self]
- end
-
- return WrapperClass
-end
-
-local function WidgetWrapper(className, WrappingParentClass)
-
- local wrappedClasses = {}
-
- local function wrap(WrappedChildClass)
- local c = wrappedClasses[WrappedChildClass]
- if not c then
- c = newWrapperClass(className,
- WrappedChildClass,
- WrappingParentClass,
- { "frame" }, { "animateFrame", "setVisible", "isVisible", "triggerLayout" })
- wrappedClasses[WrappedChildClass] = c
- end
- return c
- end
- return wrap
-end
-
-return WidgetWrapper
diff --git a/src/lwtk/Window.lua b/src/lwtk/Window.lua
index 75a018f..f5cd865 100644
--- a/src/lwtk/Window.lua
+++ b/src/lwtk/Window.lua
@@ -1,6 +1,7 @@
local lwtk = require"lwtk"
local floor = math.floor
+local rawset = rawset
local Area = lwtk.Area
local ChildLookup = lwtk.ChildLookup
@@ -11,7 +12,7 @@ local getMeasures = lwtk.layout.getMeasures
local callRelayout = lwtk.layout.callRelayout
local setOuterMargins = lwtk.layout.setOuterMargins
-local Super = lwtk.MouseDispatcher(lwtk.KeyHandler(lwtk.Styleable(lwtk.Object)))
+local Super = lwtk.MouseDispatcher(lwtk.KeyHandler(lwtk.Styleable(lwtk.Drawable(lwtk.Node(lwtk.Actionable())))))
local Window = lwtk.newClass("lwtk.Window", Super)
Window.triggerLayout = lwtk.Component.triggerLayout
@@ -19,9 +20,31 @@ Window.triggerRedraw = lwtk.Component.triggerRedraw
Window.getRoot = lwtk.Component.getRoot
Window.childById = lwtk.Group.childById
-Window.color = true
+Window:declare(
+ "_hasChanges",
+ "_needsRelayout",
+ "_positionsChanged",
+ "_handleChildRequestsFocus",
+ "_grabFocus",
+ "color",
+ "damagedArea",
+ "driver",
+ "exposedArea",
+ "fullRedisplayOutstanding",
+ "hasFocus",
+ "initParams",
+ "mapped",
+ "maxH",
+ "maxSizeFixed",
+ "maxW",
+ "mouseEntered",
+ "onClose",
+ "view",
+ "onSizeRequest",
+ "onMinSizeChanged",
+ "interceptMouseDown"
+)
-local getParent = lwtk.get.parent
local getStyle = lwtk.get.style
local getApp = lwtk.get.app
local getRoot = lwtk.get.root
@@ -32,9 +55,10 @@ local extract = lwtk.extract
local childSizes = lwtk.WeakKeysTable()
-function Window:new(app, initParams)
+function Window.override:new(app, initParams)
Super.new(self)
self.fullRedisplayOutstanding = true
+ self.color = true
getApp[self] = app
getRoot[self] = self
getFontInfos[self] = getFontInfos[app]
@@ -43,9 +67,6 @@ function Window:new(app, initParams)
self.y = 0
self.w = 0
self.h = 0
- self.getCurrentTime = app.getCurrentTime
- self.setTimer = app.setTimer
- getParent[self] = app
getKeyBinding[self] = getKeyBinding[app]
getStyle[self] = getStyle[app]
Super.new(self)
@@ -82,6 +103,10 @@ function Window:new(app, initParams)
end
end
+function Window:getCurrentTime()
+ getApp[self]:getCurrentTime()
+end
+
local adjustMinMaxSize
local function realize(self)
@@ -236,14 +261,16 @@ adjustMinMaxSize = function(self, forceMaxSize)
bestW, bestH,
self.maxW, self.maxH)
else
- self:setMinSize(minW, minH)
- self:setSize(bestW, bestH)
- self:setMaxSize(maxW, maxH)
+ if bestW > 0 and bestH > 0 then
+ self:setMinSize(minW, minH)
+ self:setSize(bestW, bestH)
+ self:setMaxSize(maxW, maxH)
+ end
end
end
-function Window:addChild(child)
- self[#self + 1] = child
+function Window.implement:addChild(child)
+ rawset(self, #self + 1, child)
local focusHandler = FocusHandler(child)
getApp[focusHandler] = getApp[self]
getFocusHandler[child] = focusHandler
@@ -280,6 +307,7 @@ function Window:setFrame(x, y, w, h)
y = floor(y + 0.5)
w = floor(w + 0.5)
h = floor(h + 0.5)
+ assert(w > 0 and h > 0, "width and height must be > 0")
if self.view then
self.view:setFrame(x, y, w, h)
else
@@ -294,6 +322,7 @@ function Window:setSize(w, h)
end
w = floor(w + 0.5)
h = floor(h + 0.5)
+ assert(w > 0 and h > 0, "width and height must be > 0")
if self.view then
self.view:setSize(w, h)
else
@@ -356,7 +385,7 @@ function Window:setColor(color)
if self.color ~= color then
self.color = color
self._hasChanges = true
- local p = getParent[self]
+ local p = getApp[self]
if p then p._hasChanges = true end
end
end
@@ -404,7 +433,7 @@ end
function Window:close()
if self.view and not self.view:isClosed() then
self.view:close()
- getParent[self]:_removeWindow(self)
+ getApp[self]:_removeWindow(self)
end
end
@@ -553,8 +582,6 @@ function Window:requestClose()
end
end
-Window._handleClose = Window.requestClose
-
function Window:requestFocus()
if self.view and self.mapped then
self.driver:grabFocus(self)
@@ -627,4 +654,8 @@ function Window:_postProcessChanges()
adjustMinMaxSize(self)
end
+function Window:setTimer(seconds, func, ...)
+ getApp[self]:setTimer(seconds, func, ...)
+end
+
return Window
diff --git a/src/lwtk/btest.lua b/src/lwtk/btest.lua
index 4c24928..71985e6 100644
--- a/src/lwtk/btest.lua
+++ b/src/lwtk/btest.lua
@@ -2,7 +2,13 @@ local tryrequire = require("lwtk.tryrequire")
local lpugl = tryrequire("lpugl")
+--[[
+ @function(...)
+
+ Returns *true* if bitwise AND of its operands is different from zero.
+]]
local btest = lpugl and lpugl.btest
+
if not btest then
local bit = tryrequire("bit")
if bit then
@@ -13,6 +19,9 @@ if not btest then
end
if not btest then
+ if not lpugl then
+ require("lpugl") -- let require produce error message
+ end
error("no bitoperations found")
end
diff --git a/src/lwtk/get.lua b/src/lwtk/get.lua
index 1dfff0b..100a095 100644
--- a/src/lwtk/get.lua
+++ b/src/lwtk/get.lua
@@ -4,6 +4,8 @@ local WeakKeysTable = lwtk.WeakKeysTable
local get = {}
+get.objectMeta = WeakKeysTable()
+get.class = WeakKeysTable()
get.superClass = WeakKeysTable()
get.app = WeakKeysTable()
get.root = WeakKeysTable()
@@ -13,9 +15,6 @@ get.focusHandler = WeakKeysTable()
get.parentFocusHandler = WeakKeysTable()
get.focusedChild = WeakKeysTable()
-get.wrapper = WeakKeysTable()
-get.wrappedChild = WeakKeysTable()
-get.wrappingParent = WeakKeysTable()
get.focusableChildren = WeakKeysTable()
get.actions = WeakKeysTable()
get.keyBinding = WeakKeysTable()
@@ -24,5 +23,6 @@ get.fontInfos = WeakKeysTable()
get.childLookup = WeakKeysTable()
get.visibilityChanges = WeakKeysTable()
get.deferredChanges = WeakKeysTable()
+get.mixinBase = WeakKeysTable()
return get
diff --git a/src/lwtk/init.lua b/src/lwtk/init.lua
index d937802..46e6f4c 100644
--- a/src/lwtk/init.lua
+++ b/src/lwtk/init.lua
@@ -1,3 +1,6 @@
+--[[
+ Root module for all other *lwtk* modules.
+]]
local lwtk = {}
lwtk.MOD_SHIFT = 1
diff --git a/src/lwtk/internal/ColumnImpl.lua b/src/lwtk/internal/ColumnImpl.lua
index 123ad6d..2cf58a4 100644
--- a/src/lwtk/internal/ColumnImpl.lua
+++ b/src/lwtk/internal/ColumnImpl.lua
@@ -9,8 +9,8 @@ local calculateTBMeasures = lwtk.internal.LayoutImpl.calculateTBMeasures
local applyLRLayout = lwtk.internal.LayoutImpl.applyLRLayout
local applyTBLayout = lwtk.internal.LayoutImpl.applyTBLayout
-local lr4Caches = setmetatable({}, { __mode = "k" })
-local tb4Caches = setmetatable({}, { __mode = "k" })
+local lr4Caches = lwtk.WeakKeysTable()
+local tb4Caches = lwtk.WeakKeysTable()
-------------------------------------------------------------------------------------------------
@@ -72,13 +72,15 @@ end
-------------------------------------------------------------------------------------------------
-function ColumnImpl.implementColumn(class, isRow)
+function ColumnImpl.implementColumn(isRow)
+ local impl = {}
+
local getChildLRMeasures = isRow and getChildLRMeasuresForRow
or getChildLRMeasuresForColumn
local getChildTBMeasures = isRow and getChildTBMeasuresForRow
or getChildTBMeasuresForColumn
- function class:getMeasures()
+ function impl:getMeasures()
local minWidth, bestWidth, maxWidth,
leftMargin, rightMargin = calculateLRMeasures(self, getChildLRMeasures)
@@ -102,7 +104,7 @@ function ColumnImpl.implementColumn(class, isRow)
topMargin, rightMargin, bottomMargin, leftMargin
end
- function class:onLayout(width, height, isLayoutTransition)
+ function impl:onLayout(width, height, isLayoutTransition)
if not isLayoutTransition then
local topMargin, rightMargin, bottomMargin, leftMargin = getOuterMargins(self)
if isRow then
@@ -130,6 +132,8 @@ function ColumnImpl.implementColumn(class, isRow)
end
end
end
+
+ return impl
end
diff --git a/src/lwtk/internal/StyleRuleContext.lua b/src/lwtk/internal/StyleRuleContext.lua
index a8f43f8..f0050b6 100644
--- a/src/lwtk/internal/StyleRuleContext.lua
+++ b/src/lwtk/internal/StyleRuleContext.lua
@@ -6,6 +6,15 @@ local errorf = lwtk.errorf
local StyleRuleContext = lwtk.newClass("lwtk.internal.StyleRuleContext")
+StyleRuleContext:declare(
+ "ctxRules",
+ "style",
+ "classSelectorPath",
+ "stateSelectorPath",
+ "localParams",
+ "localInvolved"
+)
+
function StyleRuleContext:new(ctxRules, style, classSelectorPath, stateSelectorPath, localParams)
self.ctxRules = ctxRules
self.style = style
diff --git a/src/lwtk/internal/TypeRule.lua b/src/lwtk/internal/TypeRule.lua
index 0cb40d2..1a4acb4 100644
--- a/src/lwtk/internal/TypeRule.lua
+++ b/src/lwtk/internal/TypeRule.lua
@@ -5,9 +5,9 @@ local lower = string.lower
local gsub = string.gsub
local errorf = lwtk.errorf
-local TypeRule = {}
+local StyleTypeAttributes = lwtk.StyleTypeAttributes
-local toAttrName = lwtk.StyleTypeAttributes.toAttrName
+local TypeRule = {}
function TypeRule.toPattern(rule)
local n = #rule
@@ -15,9 +15,8 @@ function TypeRule.toPattern(rule)
local result = {}
for i = 1, n do
local arg = rule[i]
- local name = toAttrName[arg]
- if name then
- result[name] = true
+ if StyleTypeAttributes[arg] then
+ result[arg] = true
else
result[#result + 1] = arg
end
diff --git a/src/lwtk/internal/utf8string.lua b/src/lwtk/internal/utf8string.lua
index 33c910b..c7281cf 100644
--- a/src/lwtk/internal/utf8string.lua
+++ b/src/lwtk/internal/utf8string.lua
@@ -98,7 +98,7 @@ end
local module = {
dump = string.dump,
byte = string.byte,
- code = utf8.codepoint,
+-- code = utf8.codepoint,
char = utf8.char,
format = string.format,
rep = string.rep,
diff --git a/src/lwtk/vivid.lua b/src/lwtk/internal/vivid.lua
similarity index 100%
rename from src/lwtk/vivid.lua
rename to src/lwtk/internal/vivid.lua
diff --git a/src/lwtk/isInstanceOf.lua b/src/lwtk/isInstanceOf.lua
index 0137536..aa47e36 100644
--- a/src/lwtk/isInstanceOf.lua
+++ b/src/lwtk/isInstanceOf.lua
@@ -2,15 +2,26 @@ local lwtk = require"lwtk"
local getSuperClass = lwtk.get.superClass
-local function isInstanceOf(obj, T)
- local mt = getmetatable(obj)
- while mt do
- if mt == T then
- return true
+--[[
+ Determines if object is instance of the given class.
+
+ Returns *true*, if *self* is an object that was created by invoking class *C*
+ or by invoking a subclass of *C*.
+
+ Returns also *true* if *C* is a metatable of *self* or somewhere in the
+ metatable chain of *self*.
+]]
+local function isInstanceOf(self, C)
+ local c = getmetatable(self)
+ if c then
+ repeat
+ if c == C then
+ return true
+ end
+ c = getSuperClass[c]
+ until not c
end
- mt = getSuperClass[mt]
- end
- return false
+ return false
end
return isInstanceOf
diff --git a/src/lwtk/layout.lua b/src/lwtk/layout.lua
index 8fcacf9..9cf6a29 100644
--- a/src/lwtk/layout.lua
+++ b/src/lwtk/layout.lua
@@ -94,7 +94,8 @@ end
local function collectOldFrames(widget, oldFrames)
- for _, c in ipairs(widget) do
+ for i = 1, #widget do
+ local c = widget[i]
oldFrames[c] = { c.x, c.y, c.w, c.h }
c._isRelayouting = true
collectOldFrames(c, oldFrames)
diff --git a/src/lwtk/love/Application.lua b/src/lwtk/love/Application.lua
index bf72793..71f4b4e 100644
--- a/src/lwtk/love/Application.lua
+++ b/src/lwtk/love/Application.lua
@@ -1,77 +1,28 @@
local lwtk = require"lwtk"
-local getApp = lwtk.get.app
+local getApp = lwtk.get.app
+local keyNameMap = lwtk.love.keyNameMap
-local Super = lwtk.MouseDispatcher(lwtk.Application)
-local Application = lwtk.newClass("lwtk.love.Application", Super)
+local Super = lwtk.MouseDispatcher(lwtk.Node(lwtk.Application))
-local keyNameMap =
-{
- space = "Space",
- up = "Up",
- down = "Down",
- right = "Right",
- left = "Left",
- home = "Home",
- ["end"] = "End",
- pageup = "Page_Up",
- pagedown = "Page_Down",
- insert = "Insert",
- backspace = "Backspace",
- tab = "Tab",
- clear = "Clear",
- ["return"] = "Return",
- delete = "Delete",
- escape = "Escape",
-
- kp0 = "KP_0",
- kp1 = "KP_1",
- kp2 = "KP_2",
- kp3 = "KP_3",
- kp4 = "KP_4",
- kp5 = "KP_5",
- kp6 = "KP_6",
- kp7 = "KP_7",
- kp8 = "KP_8",
- kp9 = "KP_9",
- kpenter = "KP_Enter",
- ["kp+"] = "KP_Add",
- ["kp-"] = "KP_Subtract",
- ["kp/"] = "KP_Divide",
- ["kp,"] = "KP_Separator",
- ["kp."] = "KP_Separator",
+--[[
+ Application implementation for the [LÖVE](https://love2d.org/) 2D game engine.
- f1 = "F1",
- f2 = "F2",
- f3 = "F3",
- f4 = "F4",
- f5 = "F5",
- f6 = "F6",
- f7 = "F7",
- f8 = "F8",
- f9 = "F9",
- f10 = "F10",
- f11 = "F11",
- f12 = "F12",
- f13 = "F13",
- f14 = "F14",
- f15 = "F15",
- f16 = "F16",
- f17 = "F17",
- f18 = "F18",
+ Use lwtk.Application for runing standalone desktop applications.
+]]
+local Application = lwtk.newClass("lwtk.love.Application", Super)
+
+Application:declare(
+ "_implicitWindowFocus",
+ "_keyModState",
+ "modKeyToBit",
+ "mouseEntered",
+ "focusWindow",
+ "_handledKey"
+)
- rshift = "Shift_R",
- lshift = "Shift_L",
- rctrl = "Ctrl_R",
- lctrl = "Ctrl_L",
- ralt = "Alt_R",
- lalt = "Alt_L",
- rgui = "Super_R",
- lgui = "Super_L",
- capslock = "Caps_Lock",
-}
-function Application:new(initParams)
+function Application.override:new(initParams)
if not initParams then
initParams = {}
end
@@ -98,7 +49,7 @@ end
local setFocusWindow
-function Application:update()
+function Application.override:update()
local driver = self.driver
do
local newWindows = driver.newWindows
diff --git a/src/lwtk/love/DrawContext.lua b/src/lwtk/love/DrawContext.lua
index d97cfa8..339a789 100644
--- a/src/lwtk/love/DrawContext.lua
+++ b/src/lwtk/love/DrawContext.lua
@@ -3,13 +3,18 @@ local lwtk = require"lwtk"
local Super = lwtk.love.LayoutContext
local DrawContext = lwtk.newClass("lwtk.love.DrawContext", Super)
-function DrawContext:new(...)
+DrawContext:declare(
+ "opacityStack",
+ "scissorStack"
+)
+
+function DrawContext.override:new(...)
Super.new(self, ...)
self.opacityStack = {}
self.scissorStack = {}
end
-function DrawContext:_reset()
+function DrawContext.override:_reset()
Super._reset()
local opacityStack = self.opacityStack
for k, v in pairs(opacityStack) do
diff --git a/src/lwtk/love/Driver.lua b/src/lwtk/love/Driver.lua
index 60a652c..36092d0 100644
--- a/src/lwtk/love/Driver.lua
+++ b/src/lwtk/love/Driver.lua
@@ -5,6 +5,15 @@ local View = lwtk.love.View
local Driver = lwtk.newClass("lwtk.love.Driver")
+Driver:declare(
+ "drawContext",
+ "layoutContext",
+ "newWindows",
+ "processFunc",
+ "nextProcessTime",
+ "hasViews"
+)
+
function Driver:new(initParams)
self.drawContext = lwtk.love.DrawContext()
self.layoutContext = lwtk.love.LayoutContext()
diff --git a/src/lwtk/love/LayoutContext.lua b/src/lwtk/love/LayoutContext.lua
index f740582..d5f2629 100644
--- a/src/lwtk/love/LayoutContext.lua
+++ b/src/lwtk/love/LayoutContext.lua
@@ -4,6 +4,13 @@ local floor = math.floor
local LayoutContext = lwtk.newClass("lwtk.love.LayoutContext")
+LayoutContext:declare(
+ "fonts",
+ "font",
+ "fontHeight",
+ "fontAscent"
+)
+
function LayoutContext:new()
love.graphics.reset()
self.fonts = {}
diff --git a/src/lwtk/love/View.lua b/src/lwtk/love/View.lua
index 4407bd2..da0ddfa 100644
--- a/src/lwtk/love/View.lua
+++ b/src/lwtk/love/View.lua
@@ -3,9 +3,16 @@ local lwtk = require"lwtk"
local floor = math.floor
local unpack = unpack or table.unpack
+local getWindow = lwtk.WeakKeysTable()
+
local View = lwtk.newClass("lwtk.love.View")
-local getWindow = lwtk.WeakKeysTable()
+View:declare(
+ "x", "y", "w", "h",
+ "damagedArea",
+ "canvas",
+ "closed"
+)
function View:new(window, initParams)
getWindow[self] = window
diff --git a/src/lwtk/love/init.lua b/src/lwtk/love/init.lua
index 9f68643..f559831 100644
--- a/src/lwtk/love/init.lua
+++ b/src/lwtk/love/init.lua
@@ -1,3 +1,6 @@
+--[[
+ Modules for using *lwtk* with the [LÖVE](https://love2d.org/) 2D game engine.
+]]
local internal = {}
diff --git a/src/lwtk/love/keyNameMap.lua b/src/lwtk/love/keyNameMap.lua
new file mode 100644
index 0000000..5bf69ca
--- /dev/null
+++ b/src/lwtk/love/keyNameMap.lua
@@ -0,0 +1,67 @@
+local keyNameMap =
+{
+ space = "Space",
+ up = "Up",
+ down = "Down",
+ right = "Right",
+ left = "Left",
+ home = "Home",
+ ["end"] = "End",
+ pageup = "Page_Up",
+ pagedown = "Page_Down",
+ insert = "Insert",
+ backspace = "Backspace",
+ tab = "Tab",
+ clear = "Clear",
+ ["return"] = "Return",
+ delete = "Delete",
+ escape = "Escape",
+
+ kp0 = "KP_0",
+ kp1 = "KP_1",
+ kp2 = "KP_2",
+ kp3 = "KP_3",
+ kp4 = "KP_4",
+ kp5 = "KP_5",
+ kp6 = "KP_6",
+ kp7 = "KP_7",
+ kp8 = "KP_8",
+ kp9 = "KP_9",
+ kpenter = "KP_Enter",
+ ["kp+"] = "KP_Add",
+ ["kp-"] = "KP_Subtract",
+ ["kp/"] = "KP_Divide",
+ ["kp,"] = "KP_Separator",
+ ["kp."] = "KP_Separator",
+
+ f1 = "F1",
+ f2 = "F2",
+ f3 = "F3",
+ f4 = "F4",
+ f5 = "F5",
+ f6 = "F6",
+ f7 = "F7",
+ f8 = "F8",
+ f9 = "F9",
+ f10 = "F10",
+ f11 = "F11",
+ f12 = "F12",
+ f13 = "F13",
+ f14 = "F14",
+ f15 = "F15",
+ f16 = "F16",
+ f17 = "F17",
+ f18 = "F18",
+
+ rshift = "Shift_R",
+ lshift = "Shift_L",
+ rctrl = "Ctrl_R",
+ lctrl = "Ctrl_L",
+ ralt = "Alt_R",
+ lalt = "Alt_L",
+ rgui = "Super_R",
+ lgui = "Super_L",
+ capslock = "Caps_Lock",
+}
+
+return keyNameMap
diff --git a/src/lwtk/lpugl/CairoDrawContext.lua b/src/lwtk/lpugl/CairoDrawContext.lua
index 2cdca79..14c148a 100644
--- a/src/lwtk/lpugl/CairoDrawContext.lua
+++ b/src/lwtk/lpugl/CairoDrawContext.lua
@@ -3,7 +3,11 @@ local lwtk = require"lwtk"
local Super = lwtk.lpugl.CairoLayoutContext
local CairoDrawContext = lwtk.newClass("lwtk.lpugl.CairoDrawContext", Super)
-function CairoDrawContext:new(...)
+CairoDrawContext:declare(
+ "opacityStack"
+)
+
+function CairoDrawContext.override:new(...)
Super.new(self, ...)
self.opacityStack = {}
end
diff --git a/src/lwtk/lpugl/CairoLayoutContext.lua b/src/lwtk/lpugl/CairoLayoutContext.lua
index 1c67927..a75091d 100644
--- a/src/lwtk/lpugl/CairoLayoutContext.lua
+++ b/src/lwtk/lpugl/CairoLayoutContext.lua
@@ -4,6 +4,12 @@ local floor = math.floor
local CairoLayoutContext = lwtk.newClass("lwtk.lpugl.CairoLayoutContext")
+CairoLayoutContext:declare(
+ "ctx",
+ "platform",
+ "adjustFamilyName"
+)
+
function CairoLayoutContext:new(cairoCtx, platform)
self.ctx = assert(cairoCtx)
self.platform = assert(platform)
diff --git a/src/lwtk/lpugl/Driver.lua b/src/lwtk/lpugl/Driver.lua
index 59cc238..8b5fe73 100644
--- a/src/lwtk/lpugl/Driver.lua
+++ b/src/lwtk/lpugl/Driver.lua
@@ -15,6 +15,12 @@ local extract = lwtk.extract
local Driver = lwtk.newClass("lwtk.lpugl.Driver")
+Driver:declare(
+ "world",
+ "layoutContext",
+ "drawContext"
+)
+
function Driver:new(initParams)
self.world = extract(initParams, "world")
if not self.world then
diff --git a/src/lwtk/lpugl/init.lua b/src/lwtk/lpugl/init.lua
index e86d832..3104382 100644
--- a/src/lwtk/lpugl/init.lua
+++ b/src/lwtk/lpugl/init.lua
@@ -1,3 +1,7 @@
+--[[
+ Modules for using *lwtk* on top of [LPugl](https://github.com/osch/lua-lpugl#lpugl),
+ a minimal Lua-API for building GUIs for Linux, Windows or macOS.
+]]
local internal = {}
diff --git a/src/lwtk/newClass.lua b/src/lwtk/newClass.lua
index 32cd946..173680b 100644
--- a/src/lwtk/newClass.lua
+++ b/src/lwtk/newClass.lua
@@ -1,13 +1,28 @@
local lwtk = require"lwtk"
-local type = lwtk.type
-local function newClass(className, baseClass, ...)
+local ltype = lwtk.type
+
+--[[
+ Creates new class object. A class object has lwtk.Class as metatable
+ and lwtk.type() evaluates to `"lwtk.Class"`.
+
+ * *className* - string value
+ * *superClass* - optional class object, if not given, lwtk.Object
+ is taken as superclass.
+ * *...* - optional further arguments that are reached over
+ to [lwtk.Object.newSubClass()](../lwtk/Object.md#.newSubClass).
+
+ See [lwtk.Class Usage](../../Class.md) for detailed documentation
+ and usage examples.
+]]
+local function newClass(className, superClass, ...)
assert(type(className) == "string", "arg 1: exptected class name string")
- if baseClass then
- assert(type(baseClass) == "lwtk.Class", "arg 2 must be of type lwtk.Class")
+ assert(not className:match("[()<>]"), "arg 1: invalid class name string")
+ if superClass then
+ assert(ltype(superClass) == "lwtk.Class", "arg 2 must be of type lwtk.Class")
end
- local b = baseClass or lwtk.Object
- local c = b.newSubClass(className, b, ...)
+ local b = superClass or lwtk.Object
+ local c = b:newSubClass(className, ...)
return c
end
diff --git a/src/lwtk/newMeta.lua b/src/lwtk/newMeta.lua
new file mode 100644
index 0000000..22b1717
--- /dev/null
+++ b/src/lwtk/newMeta.lua
@@ -0,0 +1,21 @@
+local lwtk = require"lwtk"
+
+local Meta = lwtk.Meta
+local fallbackToString = Meta.fallbackToString
+
+--[[
+ Creates new meta object. A meta object has lwtk.Meta as metatable
+ and lwtk.type() evaluates to `"lwtk.Meta"`.
+
+ See [lwtk.Meta Usage](../../Meta.md) for detailed documentation
+ and usage examples.
+]]
+local function newMeta(name)
+ local meta = {
+ __name = name,
+ __tostring = fallbackToString
+ }
+ return setmetatable(meta, Meta)
+end
+
+return newMeta
diff --git a/src/lwtk/newMixin.lua b/src/lwtk/newMixin.lua
index 12eff98..59a7062 100644
--- a/src/lwtk/newMixin.lua
+++ b/src/lwtk/newMixin.lua
@@ -1,59 +1,166 @@
local lwtk = require("lwtk")
-local type = lwtk.type
-local caches = lwtk.WeakKeysTable()
-local mixins = lwtk.WeakKeysTable()
-local unpack = table.unpack or unpack
+local unpack = table.unpack or unpack
+local format = string.format
+local ltype = lwtk.type
+local getSuperClass = lwtk.get.superClass
+local getObjectMeta = lwtk.get.objectMeta
+local getMixinBase = lwtk.get.mixinBase
+
+local caches = lwtk.WeakKeysTable()
+local mixins = lwtk.WeakKeysTable()
local Mixin = lwtk.Mixin
+local cacheMeta = {
+ __mode = "v"
+}
+
+--[[
+ Creates new mixin object. A mixin object has lwtk.Mixin as metatable
+ and lwtk.type() evaluates to `"lwtk.Mixin"`.
+
+ See [lwtk.Mixin Usage](../../Mixin.md) for detailed documentation
+ and usage examples.
+]]
local function newMixin(name, ...)
- assert(type(name) == "string", "arg 1 must be string")
+ assert(ltype(name) == "string", "arg 1 must be string")
local mixin = { name = name }
local nargs = select("#", ...)
for i = 1, nargs do
local arg = select(i, ...)
- if type(arg) ~= "lwtk.Mixin" then
+ if ltype(arg) ~= "lwtk.Mixin" then
break
end
mixin[i] = arg
end
mixin.cargs = { nargs = nargs - #mixin,
select(#mixin + 1, ...) }
- local self = setmetatable({}, Mixin)
- mixins[self] = mixin
- caches[self] = setmetatable({}, { __mode = "v" })
- return self
+ local m = setmetatable({}, Mixin)
+ m.__name = name
+ mixins[m] = mixin
+ caches[m] = setmetatable({}, cacheMeta)
+ return m
end
-function Mixin.__index:isInstanceOf(mt)
+local method = {}
+
+function method:isInstanceOf(mt)
return lwtk.isInstanceOf(self, mt)
end
+function method:declare(...)
+ local m = mixins[self].declare
+ if not m then
+ m = {}
+ mixins[self].declare = m
+ end
+ local n = #m
+ for i = 1, select("#", ...) do
+ m[n + i] = select(i, ...)
+ end
+end
+
+function Mixin:__index(k)
+ local v = method[k]
+ if v ~= nil then
+ return v
+ elseif k == "extra" or k == "override" or k == "implement" then
+ v = {}
+ self[k] = v
+ return v
+ else
+ v = self.override[k]
+ if v == nil then
+ v = self.implement[k]
+ end
+ return v
+ end
+end
+
function Mixin:__tostring()
- return "lwtk.Mixin("..mixins[self].name..")"
+ return "lwtk.Mixin<"..mixins[self].name..">"
end
function Mixin:__call(baseClass, ...)
- assert(type(baseClass) == "lwtk.Class", "arg must be of type lwtk.Class, but is "..type(baseClass))
+ if baseClass ~= nil then
+ assert(ltype(baseClass) == "lwtk.Class", "arg must be of type lwtk.Class, but is "..ltype(baseClass))
+ else
+ baseClass = lwtk.Object
+ end
+ local mixin = mixins[self]
+ for i = #mixin, 1, -1 do
+ baseClass = mixin[i](baseClass)
+ end
local cache = caches[self]
local class = cache[baseClass]
if not class then
- local mixin = mixins[self]
- for i = #mixin, 1, -1 do
- baseClass = mixin[i](baseClass)
+ do
+ local s = baseClass
+ while s do
+ if getMixinBase[s] == self then
+ lwtk.errorf("Mixin %q is already involved in superclass %q", mixin.name, baseClass:getClassPath())
+ end
+ s = getSuperClass[s]
+ end
end
local cargs = mixin.cargs
- class = baseClass.newSubClass(mixin.name, baseClass, unpack(cargs, 1, cargs.nargs))
+ local nargs = cargs.nargs
+ local lastArg = (nargs > 0) and cargs[nargs]
+ local initClass
+ if lastArg and ltype(lastArg) == "function" then
+ nargs = nargs - 1
+ initClass = lastArg
+ end
+ local baseName = (baseClass == lwtk.Object) and "" or baseClass.__name
+ class = baseClass:newSubClass(format("%s(%s)", mixin.name, baseName), unpack(cargs, 1, nargs))
+ local declare = mixin.declare
+ if declare then
+ for i = 1, #declare do
+ local var = declare[i]
+ if var:match("^[a-zA-Z_][a-zA-Z_0-9]*$") then
+ local objMeta = getObjectMeta[class]
+ if objMeta[var] == nil then
+ class[var] = false
+ else
+ local c = class
+ local m = objMeta
+ while true do
+ if m.declared[var] then
+ lwtk.errorf("member %q from mixin %q is already declared in superclass %q in class path %s", var, mixin.name, m.__name, class:getClassPath())
+ end
+ c = getSuperClass[c]
+ m = getObjectMeta[c]
+ end
+ end
+ else
+ lwtk.errorf("invalid member name %q in Mixin %s", var, mixin.name)
+ end
+ end
+ end
for k, v in pairs(self) do
- if k ~= "initClass" and k ~= "extra" then
+ if k ~= "extra" and k ~= "override" and k ~= "implement" and k ~= "__name" then
class[k] = v
end
end
- local initClass = self.initClass
+ local myOverride = self.override
+ if myOverride then
+ local classOverride = class.override
+ for k, v in pairs(myOverride) do
+ classOverride[k] = v
+ end
+ end
+ local myImplement = self.implement
+ if myImplement then
+ local classImplement = class.implement
+ for k, v in pairs(myImplement) do
+ classImplement[k] = v
+ end
+ end
if initClass then
initClass(class, baseClass)
end
+ getMixinBase[class] = self
cache[baseClass] = class
end
return class
diff --git a/src/lwtk/type.lua b/src/lwtk/type.lua
index 96f257f..5023528 100644
--- a/src/lwtk/type.lua
+++ b/src/lwtk/type.lua
@@ -1,5 +1,18 @@
local luaType = type
+--[[
+ Returns the type name.
+
+ * If *arg* is of type *"table"* or *"userdata"* and has a metatable of
+ type *"table"* with a field *"__name"*, than the value of this field
+ is returned.
+
+ * If *arg* is of type *"table"* or *"userdata"* and has a metatable of
+ type *"string"*, than this string value is returned.
+
+ * In all other cases this functions returns the same value, as the
+ builtin Lua function *type()* would return.
+]]
local function type(arg)
local t = luaType(arg)
if t == "table" or t == "userdata" then
diff --git a/src/lwtk/undef.lua b/src/lwtk/undef.lua
new file mode 100644
index 0000000..6d556b3
--- /dev/null
+++ b/src/lwtk/undef.lua
@@ -0,0 +1,24 @@
+local lwtk = require"lwtk"
+
+local get = lwtk.get
+
+local function undef(class)
+ if type(class) == "table" then
+ local objectMeta = get.objectMeta[class]
+ for k, v in pairs(get) do
+ v[class] = nil
+ if objectMeta then
+ v[objectMeta] = nil
+ end
+ end
+ if objectMeta then
+ for k, v in pairs(objectMeta) do
+ objectMeta[k] = nil
+ end
+ end
+ for k, v in pairs(class) do
+ class[k] = nil
+ end
+ end
+end
+return undef
diff --git a/src/mkdoc/README.md b/src/mkdoc/README.md
new file mode 100644
index 0000000..8551c4e
--- /dev/null
+++ b/src/mkdoc/README.md
@@ -0,0 +1,18 @@
+Requires:
+
+- lpeglabel:
+ https://luarocks.org/modules/sergio-medeiros/lpeglabel
+ https://github.com/sqmedeiros/lpeglabel
+
+- lpath:
+ https://luarocks.org/modules/xavier-wang/lpath
+
+
+Contains:
+
+- modified pprint, original code:
+ https://github.com/jagt/pprint.lua
+
+- modified lua-parser, original code:
+ https://github.com/andremm/lua-parser
+
diff --git a/src/mkdoc/comments.lua b/src/mkdoc/comments.lua
new file mode 100644
index 0000000..fd5483c
--- /dev/null
+++ b/src/mkdoc/comments.lua
@@ -0,0 +1,289 @@
+local lpeg = require "lpeglabel" -- https://luarocks.org/modules/sergio-medeiros/lpeglabel
+local re = require "relabel" -- https://luarocks.org/modules/sergio-medeiros/lpeglabel
+
+local byte = string.byte
+
+lpeg.locale(lpeg)
+
+local P, S, V = lpeg.P, lpeg.S, lpeg.V
+local C, Carg, Cb, Cc = lpeg.C, lpeg.Carg, lpeg.Cb, lpeg.Cc
+local Cf, Cg, Cmt, Cp, Cs, Ct = lpeg.Cf, lpeg.Cg, lpeg.Cmt, lpeg.Cp, lpeg.Cs, lpeg.Ct
+local Lc, T = lpeg.Lc, lpeg.T
+
+local alpha, digit, alnum = lpeg.alpha, lpeg.digit, lpeg.alnum
+local xdigit = lpeg.xdigit
+local space = lpeg.space
+
+local comments = {}
+do
+ local globalPrint = print
+ local print = globalPrint
+ local list
+ local startPosMap
+ local endPosMap
+ local subject
+
+ function comments.init(fileContent, myPrint)
+ print = myPrint or globalPrint
+ list = {}
+ startPosMap = {}
+ endPosMap = {}
+ subject = fileContent
+ end
+
+ local LF = byte("\n")
+
+ local function beginOfLine(pos)
+ for i = pos-1, 2, -1 do
+ if byte(subject, i) == LF then
+ return i+1
+ end
+ end
+ return 1
+ end
+
+ local function column(pos)
+ return pos - beginOfLine(pos) + 1
+ end
+
+ function comments.add(pos1, pos2, end_pos1, end_pos2, comment)
+ local entry = { pos1 = pos1,
+ pos2 = pos2,
+ end_pos1 = end_pos1,
+ end_pos2 = end_pos2,
+ comment = comment }
+ list[#list + 1] = entry
+ assert(not startPosMap[pos1])
+ assert(not endPosMap[end_pos2])
+ startPosMap[pos1] = entry
+ endPosMap[end_pos2] = entry
+ print("PPP", pos1, pos2, end_pos1, end_pos2, comment)
+ end
+
+ function comments.sort()
+ table.sort(list, function(a,b) return a.pos1 < b.pos1 end)
+ for i = 1, #list do
+ list[i].i = i
+ end
+ end
+
+ local notLfSpace = -P"\n" * space
+ local singleCmt = P"--" * -(P"[" * P"="^0 * P"[")
+ local exclSingleCmt = (notLfSpace^0 * P"\n" * notLfSpace^0)^1 * singleCmt
+ local exclCmt = (notLfSpace^0 * P"\n" * notLfSpace^0)
+ local blankLine = (notLfSpace^0 * P"\n" * notLfSpace^0)^2
+
+ local function isExclCmt(entry)
+ if entry.pos1 == 1 then
+ return true
+ else
+ local p = entry.pos1
+ if entry.i > 1 then
+ local pe = list[entry.i - 1]
+ if pe.end_pos2 + 1 == p then
+ p = pe.end_pos1 + 1
+ end
+ end
+ return lpeg.match(exclCmt, subject, p)
+ end
+ end
+
+ local function isSingleLineCmt(entry)
+ return lpeg.match(singleCmt, subject, entry.pos2)
+ end
+ local function isExclSingleLineCmt(entry)
+ if entry.pos1 == 1 then
+ return lpeg.match(singleCmt, subject, entry.pos2)
+ else
+ local p = entry.pos1
+ if entry.i > 1 then
+ local pe = list[entry.i - 1]
+ if pe.end_pos2 + 1 == p then
+ p = pe.end_pos1 + 1
+ end
+ end
+ return lpeg.match(exclSingleCmt, subject, p)
+ end
+ end
+
+ local function isBlankLineBefore(entry)
+ local p = entry.pos1
+ local pe = list[entry.i - 1]
+ if pe and pe.end_pos2 + 1 == p then
+ p = pe.end_pos1 + 1
+ end
+ return lpeg.match(blankLine, subject, p)
+ end
+
+ local function isBlankLineAfter(entry)
+ local rslt = lpeg.match(blankLine, subject, entry.end_pos1 + 1)
+ return rslt
+ end
+
+ local singleLineCmtBegin = P"--" * P"-"^0 * C(P(1)^0)
+
+ local function removeSingleLineCmtBegin(entry)
+ return lpeg.match(singleLineCmtBegin, entry.comment)
+ end
+
+ local multiLineCmtBegin = P"--[" * C(P"="^0) * "[" * C(space^0) * Cp() * C(P(1)^0)
+
+ local function removeMultiLineCmt(entry)
+ local eq, beginSpace, pos, rest = lpeg.match(multiLineCmtBegin, entry.comment)
+ local indent
+ if beginSpace:match("\n") then
+ indent = lpeg.match(((P"\n" * C(notLfSpace^0) * -P(1)) + P(1))^1,
+ beginSpace)
+ else
+ local col = column(entry.pos2 + pos - 1)
+ print("###################", require"inspect"{entry, pos, col})
+ indent = string.rep(" ", col - 1)
+ end
+ local rest2 = rest:gsub("\n"..indent, "\n")
+
+ print("#########", require"inspect"{rest, rest2, indent})
+ local cmtEnd = space^0 * P("]"..eq.."]")
+ return lpeg.match(C((-cmtEnd * P(1))^0) * cmtEnd * -P(1), rest2)
+ end
+
+ function comments.getBefore(p)
+ local entry = endPosMap[p - 1]
+ if entry and not isBlankLineAfter(entry) then
+ if entry then
+ if isSingleLineCmt(entry) then
+ if isExclSingleLineCmt(entry) then
+ local col = column(entry.pos2)
+ if col == column(p) then
+ local i = entry.i
+ local e = entry
+ while i-1 > 0 do
+ local pe = list[i-1]
+ if pe.end_pos2 + 1 == e.pos1 and isSingleLineCmt(pe)
+ and not isBlankLineBefore(e) and column(pe.pos2) == col
+ then
+ i = i - 1
+ e = pe
+ else
+ break
+ end
+ end
+ if isExclSingleLineCmt(list[i]) then
+ local b = {}
+ for j = i, entry.i do
+ local m = removeSingleLineCmtBegin(list[j])
+ if m then
+ b[#b + 1] = m
+ end
+ end
+ return table.concat(b, "\n")
+ else
+ --local l1 = re.calcline(subject, list[i].pos2)
+ --local l2 = re.calcline(subject, entry.end_pos1)
+ --lwtk.errorf("Ambiguous multiline comment from line %d to line %d", l1, l2)
+ end
+ end
+ end
+ else
+ if isExclCmt(entry) then
+ return removeMultiLineCmt(entry)
+ end
+ end
+ end
+ end
+ end
+
+ function comments.getAfter(p1, p2)
+ local entry = startPosMap[p1+1]
+ if not entry and p2 then
+ for p = p1+2, p2-1 do
+ entry = startPosMap[p]
+ if entry then
+ break
+ end
+ end
+ end
+ if entry then
+ for i = p1+1, entry.pos2-1 do
+ if byte(subject, i) == LF then
+ return
+ end
+ end
+ if isSingleLineCmt(entry) then
+ local col = column(entry.pos2)
+ local i = entry.i
+ local e = entry
+ while i+1 <= #list do
+ local ne = list[i+1]
+ if e.end_pos2 + 1 == ne.pos1 and isExclSingleLineCmt(ne) and col == column(ne.pos2) then
+ i = i + 1
+ e = ne
+ else
+ break
+ end
+ end
+ local b = {}
+ for j = entry.i, i do
+ local m = removeSingleLineCmtBegin(list[j])
+ if m then
+ b[#b + 1] = m
+ end
+ end
+ return table.concat(b, "\n")
+ else
+ return removeMultiLineCmt(entry)
+ end
+ end
+ end
+
+ local endSpace = space^0 * -P(1)
+ local dotSpace = P"." * space
+ local shortDocPattern = space^0 * C(((-dotSpace - blankLine - endSpace) * P(1))^0 * P"."^-1)
+
+ function comments.getShortDoc(docString)
+ if docString then
+ return lpeg.match(shortDocPattern, docString):gsub("%s+", " ")
+ end
+ end
+
+ local argListBeginPattern = Cp() * P"@function("
+ local argListPattern = (-argListBeginPattern * P(1))^0 * argListBeginPattern * Cp() * (-P")" * P(1))^0 * Cp() * ")" * Cp()
+ local argListPattern0 = P"\n" * Cp() * notLfSpace^0 * P(-1)
+ local argListPattern1 = (-argListPattern0 * P(1))^0 * argListPattern0
+ + Cp() * notLfSpace^0 * P(-1)
+ local argListPattern2 = notLfSpace^0 * (P"\n" + P(-1)) * Cp()
+
+ function comments.stripArgListDoc(docString)
+ if docString then
+ local p1, p2, p3, p4 = lpeg.match(argListPattern, docString)
+ local rsltDoc
+ local argListString
+ if p1 then
+ if p1 > 1 or p4 <= #docString then
+ local d1, d2 = docString:sub(1,p1-1), docString:sub(p4,-1)
+ local s1 = lpeg.match(argListPattern1, d1)
+ local s2 = lpeg.match(argListPattern2, d2)
+ if s1 and s2 then
+ d1 = d1:sub(1, s1-1)
+ d2 = d2:sub(s2,-1)
+ end
+ rsltDoc = (d1..d2):gsub("%s+$", "")
+ end
+ argListString = docString:sub(p2, p3-1):gsub("%s",""):gsub(",", ", ")
+ else
+ rsltDoc = docString
+ end
+ return rsltDoc, argListString
+ end
+ end
+
+ function comments.argListDocToMethod(argListDoc)
+ if argListDoc:match("^self,") then
+ return argListDoc:gsub("^self,%s*",""), true
+ elseif argListDoc:match("^self$") then
+ return "", true
+ else
+ return argListDoc
+ end
+ end
+end
+return comments
diff --git a/src/mkdoc/lua-parser/LICENSE b/src/mkdoc/lua-parser/LICENSE
new file mode 100644
index 0000000..1e1f6a6
--- /dev/null
+++ b/src/mkdoc/lua-parser/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Andre Murbach Maidl
+
+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.
diff --git a/src/mkdoc/lua-parser/parser.lua b/src/mkdoc/lua-parser/parser.lua
new file mode 100644
index 0000000..0fe8660
--- /dev/null
+++ b/src/mkdoc/lua-parser/parser.lua
@@ -0,0 +1,489 @@
+--[[
+This module implements a parser for Lua 5.3 with LPeg,
+and generates an Abstract Syntax Tree that is similar to the one generated by Metalua.
+For more information about Metalua, please, visit:
+https://github.com/fab13n/metalua-parser
+
+block: { stat* }
+
+stat:
+ `Do{ stat* }
+ | `Set{ {lhs+} {expr+} } -- lhs1, lhs2... = e1, e2...
+ | `While{ expr block } -- while e do b end
+ | `Repeat{ block expr } -- repeat b until e
+ | `If{ (expr block)+ block? } -- if e1 then b1 [elseif e2 then b2] ... [else bn] end
+ | `Fornum{ ident expr expr expr? block } -- for ident = e, e[, e] do b end
+ | `Forin{ {ident+} {expr+} block } -- for i1, i2... in e1, e2... do b end
+ | `Local{ {ident+} {expr+}? } -- local i1, i2... = e1, e2...
+ | `Localrec{ ident expr } -- only used for 'local function'
+ | `Goto{ } -- goto str
+ | `Label{ } -- ::str::
+ | `Return{ } -- return e1, e2...
+ | `Break -- break
+ | apply
+
+expr:
+ `Nil
+ | `Dots
+ | `Boolean{ }
+ | `Number{ }
+ | `String{ }
+ | `Function{ { `Id{ }* `Dots? } block }
+ | `Table{ ( `Pair{ expr expr } | expr )* }
+ | `Op{ opid expr expr? }
+ | `Paren{ expr } -- significant to cut multiple values returns
+ | apply
+ | lhs
+
+apply:
+ `Call{ expr expr* }
+ | `Invoke{ expr `String{ } expr* }
+
+lhs: `Id{ } | `Index{ expr expr }
+
+opid: -- includes additional operators from Lua 5.3 and all relational operators
+ 'add' | 'sub' | 'mul' | 'div'
+ | 'idiv' | 'mod' | 'pow' | 'concat'
+ | 'band' | 'bor' | 'bxor' | 'shl' | 'shr'
+ | 'eq' | 'ne' | 'lt' | 'gt' | 'le' | 'ge'
+ | 'and' | 'or' | 'unm' | 'len' | 'bnot' | 'not'
+]]
+
+local lpeg = require "lpeglabel"
+
+lpeg.locale(lpeg)
+
+local P, S, V = lpeg.P, lpeg.S, lpeg.V
+local C, Carg, Cb, Cc = lpeg.C, lpeg.Carg, lpeg.Cb, lpeg.Cc
+local Cf, Cg, Cmt, Cp, Cs, Ct = lpeg.Cf, lpeg.Cg, lpeg.Cmt, lpeg.Cp, lpeg.Cs, lpeg.Ct
+local Lc, T = lpeg.Lc, lpeg.T
+
+local alpha, digit, alnum = lpeg.alpha, lpeg.digit, lpeg.alnum
+local xdigit = lpeg.xdigit
+local space = lpeg.space
+
+local commentCollector
+
+-- error message auxiliary functions
+
+local labels = {
+ { "ErrExtra", "unexpected character(s), expected EOF" },
+ { "ErrInvalidStat", "unexpected token, invalid start of statement" },
+
+ { "ErrEndIf", "expected 'end' to close the if statement" },
+ { "ErrExprIf", "expected a condition after 'if'" },
+ { "ErrThenIf", "expected 'then' after the condition" },
+ { "ErrExprEIf", "expected a condition after 'elseif'" },
+ { "ErrThenEIf", "expected 'then' after the condition" },
+
+ { "ErrEndDo", "expected 'end' to close the do block" },
+ { "ErrExprWhile", "expected a condition after 'while'" },
+ { "ErrDoWhile", "expected 'do' after the condition" },
+ { "ErrEndWhile", "expected 'end' to close the while loop" },
+ { "ErrUntilRep", "expected 'until' at the end of the repeat loop" },
+ { "ErrExprRep", "expected a conditions after 'until'" },
+
+ { "ErrForRange", "expected a numeric or generic range after 'for'" },
+ { "ErrEndFor", "expected 'end' to close the for loop" },
+ { "ErrExprFor1", "expected a starting expression for the numeric range" },
+ { "ErrCommaFor", "expected ',' to split the start and end of the range" },
+ { "ErrExprFor2", "expected an ending expression for the numeric range" },
+ { "ErrExprFor3", "expected a step expression for the numeric range after ','" },
+ { "ErrInFor", "expected '=' or 'in' after the variable(s)" },
+ { "ErrEListFor", "expected one or more expressions after 'in'" },
+ { "ErrDoFor", "expected 'do' after the range of the for loop" },
+
+ { "ErrDefLocal", "expected a function definition or assignment after local" },
+ { "ErrNameLFunc", "expected a function name after 'function'" },
+ { "ErrEListLAssign", "expected one or more expressions after '='" },
+ { "ErrEListAssign", "expected one or more expressions after '='" },
+
+ { "ErrFuncName", "expected a function name after 'function'" },
+ { "ErrNameFunc1", "expected a function name after '.'" },
+ { "ErrNameFunc2", "expected a method name after ':'" },
+ { "ErrOParenPList", "expected '(' for the parameter list" },
+ { "ErrCParenPList", "expected ')' to close the parameter list" },
+ { "ErrEndFunc", "expected 'end' to close the function body" },
+ { "ErrParList", "expected a variable name or '...' after ','" },
+
+ { "ErrLabel", "expected a label name after '::'" },
+ { "ErrCloseLabel", "expected '::' after the label" },
+ { "ErrGoto", "expected a label after 'goto'" },
+ { "ErrRetList", "expected an expression after ',' in the return statement" },
+
+ { "ErrVarList", "expected a variable name after ','" },
+ { "ErrExprList", "expected an expression after ','" },
+
+ { "ErrOrExpr", "expected an expression after 'or'" },
+ { "ErrAndExpr", "expected an expression after 'and'" },
+ { "ErrRelExpr", "expected an expression after the relational operator" },
+ { "ErrBOrExpr", "expected an expression after '|'" },
+ { "ErrBXorExpr", "expected an expression after '~'" },
+ { "ErrBAndExpr", "expected an expression after '&'" },
+ { "ErrShiftExpr", "expected an expression after the bit shift" },
+ { "ErrConcatExpr", "expected an expression after '..'" },
+ { "ErrAddExpr", "expected an expression after the additive operator" },
+ { "ErrMulExpr", "expected an expression after the multiplicative operator" },
+ { "ErrUnaryExpr", "expected an expression after the unary operator" },
+ { "ErrPowExpr", "expected an expression after '^'" },
+
+ { "ErrExprParen", "expected an expression after '('" },
+ { "ErrCParenExpr", "expected ')' to close the expression" },
+ { "ErrNameIndex", "expected a field name after '.'" },
+ { "ErrExprIndex", "expected an expression after '['" },
+ { "ErrCBracketIndex", "expected ']' to close the indexing expression" },
+ { "ErrNameMeth", "expected a method name after ':'" },
+ { "ErrMethArgs", "expected some arguments for the method call (or '()')" },
+
+ { "ErrArgList", "expected an expression after ',' in the argument list" },
+ { "ErrCParenArgs", "expected ')' to close the argument list" },
+
+ { "ErrCBraceTable", "expected '}' to close the table constructor" },
+ { "ErrEqField", "expected '=' after the table key" },
+ { "ErrExprField", "expected an expression after '='" },
+ { "ErrExprFKey", "expected an expression after '[' for the table key" },
+ { "ErrCBracketFKey", "expected ']' to close the table key" },
+
+ { "ErrDigitHex", "expected one or more hexadecimal digits after '0x'" },
+ { "ErrDigitDeci", "expected one or more digits after the decimal point" },
+ { "ErrDigitExpo", "expected one or more digits for the exponent" },
+
+ { "ErrQuote", "unclosed string" },
+ { "ErrHexEsc", "expected exactly two hexadecimal digits after '\\x'" },
+ { "ErrOBraceUEsc", "expected '{' after '\\u'" },
+ { "ErrDigitUEsc", "expected one or more hexadecimal digits for the UTF-8 code point" },
+ { "ErrCBraceUEsc", "expected '}' after the code point" },
+ { "ErrEscSeq", "invalid escape sequence" },
+ { "ErrCloseLStr", "unclosed long string" },
+}
+
+local function throw(label)
+ label = "Err" .. label
+ for i, labelinfo in ipairs(labels) do
+ if labelinfo[1] == label then
+ return T(i)
+ end
+ end
+
+ error("Label not found: " .. label)
+end
+
+local function expect (patt, label)
+ return patt + throw(label)
+end
+
+
+-- regular combinators and auxiliary functions
+
+local function kw (str)
+ return P(str) * -V"IdRest"
+end
+
+local function dec(n)
+ return n - 1
+end
+
+local function tagC (tag, patt)
+ return Ct(Cg(Cp(), "pos") * Cg(Cc(tag), "tag") * patt * Cg(Cp() / dec, "end_pos"))
+end
+
+local function unaryOp (op, e)
+ return { tag = "Op", pos = e.pos, end_pos = e.end_pos, [1] = op, [2] = e }
+end
+
+local function binaryOp (e1, op, e2)
+ if not op then
+ return e1
+ else
+ return { tag = "Op", pos = e1.pos, end_pos = e2.end_pos, [1] = op, [2] = e1, [3] = e2 }
+ end
+end
+
+local function sepBy (patt, sep, label)
+ if label then
+ return patt * Cg(V"Skip" * sep * V"Skip" * expect(patt, label))^0
+ else
+ return patt * Cg(V"Skip" * sep * V"Skip" * patt)^0
+ end
+end
+
+local function chainOp (patt, sep, label)
+ return Cf(sepBy(patt, sep, label), binaryOp)
+end
+
+local function tagDo (block)
+ block.tag = "Do"
+ return block
+end
+
+local function fixFuncStat (func)
+ if func[1].is_method then table.insert(func[2][1], 1, { tag = "Id", [1] = "self" }) end
+ func[1] = {func[1]}
+ func[2] = {func[2]}
+ return func
+end
+
+local function addDots (params, dots)
+ if dots then table.insert(params, dots) end
+ return params
+end
+
+local function insertIndex (t, index)
+ return { tag = "Index", pos = t.pos, end_pos = index.end_pos, [1] = t, [2] = index }
+end
+
+local function markMethod(t, method)
+ if method then
+ return { tag = "Index", pos = t.pos, end_pos = method.end_pos, is_method = true, [1] = t, [2] = method }
+ end
+ return t
+end
+
+local function makeIndexOrCall (t1, t2)
+ if not t2 then return t1 end
+ if t2.tag == "Call" or t2.tag == "Invoke" then
+ local t = { tag = t2.tag, pos = t1.pos, end_pos = t2.end_pos, [1] = t1 }
+ for k, v in ipairs(t2) do
+ table.insert(t, v)
+ end
+ return t
+ end
+ return { tag = "Index", pos = t1.pos, end_pos = t2.end_pos, [1] = t1, [2] = t2[1] }
+end
+
+-- grammar
+local G = { V"Lua",
+ Lua = V"Shebang"^-1 * V"Skip" * V"Block" * expect(P(-1), "Extra");
+ Shebang = P"#!" * (P(1) - P"\n")^0;
+
+ Block = tagC("Block", V"Stat"^0 * V"RetStat"^-1);
+ Stat = V"IfStat" + V"DoStat" + V"WhileStat" + V"RepeatStat" + V"ForStat"
+ + V"LocalStat" + V"FuncStat" + V"BreakStat" + V"LabelStat" + V"GoToStat"
+ + V"FuncCall" * V"Skip" + V"Assignment" + P(";") * V"Skip" + -V"BlockEnd" * throw("InvalidStat");
+ BlockEnd = P"return" + "end" + "elseif" + "else" + "until" + -1;
+
+ IfStat = tagC("If", V"IfPart" * V"ElseIfPart"^0 * V"ElsePart"^-1 * expect(kw("end") * V"Skip", "EndIf"));
+ IfPart = kw("if") * V"Skip" * expect(V"Expr", "ExprIf") * V"Skip" * expect(kw("then") * V"Skip", "ThenIf") * V"Block";
+ ElseIfPart = kw("elseif") * V"Skip" * expect(V"Expr", "ExprEIf") * V"Skip" * expect(kw("then") * V"Skip", "ThenEIf") * V"Block";
+ ElsePart = kw("else") * V"Skip" * V"Block";
+
+ DoStat = kw("do") * V"Skip" * V"Block" * expect(kw("end") * V"Skip", "EndDo") / tagDo;
+ WhileStat = tagC("While", kw("while") * V"Skip" * expect(V"Expr", "ExprWhile") * V"Skip" * V"WhileBody");
+ WhileBody = expect(kw("do") * V"Skip", "DoWhile") * V"Block" * expect(kw("end") * V"Skip", "EndWhile");
+ RepeatStat = tagC("Repeat", kw("repeat") * V"Skip" * V"Block" * expect(kw("until") * V"Skip", "UntilRep") * expect(V"Expr", "ExprRep")) * V"Skip" ;
+
+ ForStat = kw("for") * V"Skip" * expect(V"ForNum" + V"ForIn", "ForRange") * expect(kw("end") * V"Skip", "EndFor");
+ ForNum = tagC("Fornum", V"Id" * V"Skip" * P("=") * V"Skip" * V"NumRange" * V"ForBody");
+ NumRange = expect(V"Expr", "ExprFor1") * V"Skip" * expect(P(","), "CommaFor") * V"Skip" * expect(V"Expr", "ExprFor2") * V"Skip"
+ * (P(",") * V"Skip" * expect(V"Expr", "ExprFor3"))^-1 * V"Skip";
+ ForIn = tagC("Forin", V"NameList" * expect(kw("in") * V"Skip", "InFor") * expect(V"ExprList", "EListFor") * V"Skip" * V"ForBody");
+ ForBody = expect(kw("do") * V"Skip", "DoFor") * V"Block";
+
+-- LocalStat = kw("local") * V"Skip" * expect(V"LocalFunc" + V"LocalAssign", "DefLocal");
+-- LocalFunc = tagC("Localrec", kw("function") * V"Skip" * expect(V"Id", "NameLFunc") * V"Skip" * V"FuncBody") / fixFuncStat * V"Skip";
+-- LocalAssign = tagC("Local", V"NameList" * (P("=") * V"Skip" * expect(V"ExprList", "EListLAssign") + Ct(Cc()))) * V"Skip";
+
+ LocalStat = V"LocalFunc" + V"LocalAssign" + kw("local") * V"Skip" * throw ("DefLocal");
+ LocalFunc = tagC("Localrec", kw("local") * V"Skip" * kw("function") * V"Skip" * expect(V"Id", "NameLFunc") * V"Skip" * V"FuncBody") / fixFuncStat * V"Skip";
+ LocalAssign = tagC("Local", kw("local") * V"Skip" * V"NameList" * (P("=") * V"Skip" * expect(V"ExprList", "EListLAssign") + Ct(Cc()))) * V"Skip";
+
+ Assignment = tagC("Set", V"VarList" * P("=") * V"Skip" * expect(V"ExprList", "EListAssign")) * V"Skip";
+
+ FuncStat = tagC("Set", kw("function") * V"Skip" * expect(V"FuncName", "FuncName") * V"FuncBody") / fixFuncStat * V"Skip";
+ FuncName = Cf(V"Id" * (V"Skip" * P(".") * V"Skip" * expect(V"StrId", "NameFunc1") * V"Skip")^0, insertIndex) * V"Skip"
+ * (P(":") * V"Skip" * expect(V"StrId", "NameFunc2") * V"Skip")^-1 / markMethod;
+ FuncBody = tagC("Function", V"FuncParams" * V"Block" * expect(kw("end"), "EndFunc"));
+ FuncParams = expect(P("(") * V"Skip", "OParenPList") * V"ParList" * expect(P(")") * V"Skip", "CParenPList");
+ ParList = V"NameList" * (P(",") * V"Skip" * expect(tagC("Dots", P("...") * V"Skip"), "ParList"))^-1 / addDots
+ + Ct(tagC("Dots", P("...") * V"Skip"))
+ + Ct(Cc()); -- Cc({}) generates a bug since the {} would be shared across parses
+
+ LabelStat = tagC("Label", P("::") * V"Skip" * expect(V"Name", "Label") * V"Skip" * expect(P("::") * V"Skip", "CloseLabel"));
+ GoToStat = tagC("Goto", kw("goto") * V"Skip" * expect(V"Name", "Goto") * V"Skip");
+ BreakStat = tagC("Break", kw("break") * V"Skip");
+ RetStat = tagC("Return", kw("return") * (V"Skip" * sepBy(V"Expr", P(","), "RetList"))^-1 * (V"Skip" * P(";"))^-1) * V"Skip";
+
+ NameList = tagC("NameList", sepBy(V"Id", P(","))) * V"Skip";
+ VarList = tagC("VarList", sepBy(V"VarExpr", P(","), "VarList")) * V"Skip";
+ ExprList = tagC("ExpList", sepBy(V"Expr", P(","), "ExprList"));
+
+ Expr = V"OrExpr";
+ OrExpr = chainOp(V"AndExpr", V"OrOp", "OrExpr");
+ AndExpr = chainOp(V"RelExpr", V"AndOp", "AndExpr");
+ RelExpr = chainOp(V"BOrExpr", V"RelOp", "RelExpr");
+ BOrExpr = chainOp(V"BXorExpr", V"BOrOp", "BOrExpr");
+ BXorExpr = chainOp(V"BAndExpr", V"BXorOp", "BXorExpr");
+ BAndExpr = chainOp(V"ShiftExpr", V"BAndOp", "BAndExpr");
+ ShiftExpr = chainOp(V"ConcatExpr", V"ShiftOp", "ShiftExpr");
+ ConcatExpr = V"AddExpr" * (V"Skip" * V"ConcatOp" * V"Skip" * expect(V"ConcatExpr", "ConcatExpr"))^-1 / binaryOp;
+ AddExpr = chainOp(V"MulExpr", V"AddOp", "AddExpr");
+ MulExpr = chainOp(V"UnaryExpr", V"MulOp", "MulExpr");
+ UnaryExpr = V"UnaryOp" * V"Skip" * expect(V"UnaryExpr", "UnaryExpr") / unaryOp
+ + V"PowExpr";
+ PowExpr = V"SimpleExpr" * (V"Skip" * V"PowOp" * V"Skip" * expect(V"UnaryExpr", "PowExpr"))^-1 / binaryOp;
+
+ SimpleExpr = tagC("Number", V"Number")
+ + tagC("String", V"String")
+ + tagC("Nil", kw("nil"))
+ + tagC("Boolean", kw("false") * Cc(false))
+ + tagC("Boolean", kw("true") * Cc(true))
+ + tagC("Dots", P("..."))
+ + V"FuncDef"
+ + V"Table"
+ + V"SuffixedExpr";
+
+ FuncCall = Cmt(V"SuffixedExpr", function(s, i, exp) return exp.tag == "Call" or exp.tag == "Invoke", exp end);
+ VarExpr = Cmt(V"SuffixedExpr", function(s, i, exp) return exp.tag == "Id" or exp.tag == "Index", exp end);
+
+ SuffixedExpr = Cf(V"PrimaryExpr" * (V"Skip" * (V"Index" + V"Call"))^0, makeIndexOrCall);
+ PrimaryExpr = V"Id" * V"Skip" + tagC("Paren", P("(") * V"Skip" * expect(V"Expr", "ExprParen") * V"Skip" * expect(P(")"), "CParenExpr"));
+ Index = tagC("DotIndex", P"." * V"Skip" * -P"." * expect(V"StrId", "NameIndex"))
+ + tagC("ArrayIndex", P("[") * -S"=[" * V"Skip" * expect(V"Expr", "ExprIndex") * V"Skip" * expect(P("]"), "CBracketIndex"));
+ Call = tagC("Invoke", Cg(P(":") * -P":" * V"Skip" * expect(V"StrId", "NameMeth") * V"Skip" * expect(V"FuncArgs", "MethArgs")))
+ + tagC("Call", V"FuncArgs");
+
+ FuncDef = kw("function") * V"Skip" * V"FuncBody";
+ FuncArgs = P("(") * V"Skip" * sepBy(V"Expr", P(","), "ArgList")^-1 * V"Skip" * expect(P(")"), "CParenArgs")
+ + V"Table"
+ + tagC("String", V"String");
+
+ Table = tagC("Table", P("{") * V"Skip" * V"FieldList"^-1 * expect(P("}"), "CBraceTable"));
+ FieldList = sepBy(V"Field", V"FieldSep") * (V"Skip" * V"FieldSep")^-1 * V"Skip";
+ Field = tagC("Pair", V"FieldKey" * expect(P("=") * V"Skip", "EqField") * expect(V"Expr", "ExprField"))
+ + V"Expr";
+ FieldKey = P("[" * -P(S"=[")) * V"Skip" * expect(V"Expr", "ExprFKey") * V"Skip" * expect(P("]") * V"Skip", "CBracketFKey")
+ + V"StrId" * V"Skip" * #("=" * -P"=");
+ FieldSep = P(",") * V"Skip" + P(";") * V"Skip";
+
+ Id = tagC("Id", V"Name");
+ StrId = tagC("String", V"Name");
+
+ -- lexer
+ Skip = ((Cp() * space^0 * Cp() * C(V"Comment") * Cp() * space^0 * Cp()) / function(p1a, p1b, c, p2a, p2b)
+ commentCollector.add(p1a, p1b, p2a-1, p2b-1, c)
+ end
+ + V"Space")^0;
+
+ Space = space^1;
+ Comment = P"--" * V"LongStr" / function () return end
+ + P"--" * (P(1) - P"\n")^0;
+
+ Name = -V"Reserved" * C(V"Ident");
+ Reserved = V"Keywords" * -V"IdRest";
+ Keywords = P"and" + "break" + "do" + "elseif" + "else" + "end"
+ + "false" + "for" + "function" + "goto" + "if" + "in"
+ + "local" + "nil" + "not" + "or" + "repeat" + "return"
+ + "then" + "true" + "until" + "while";
+ Ident = V"IdStart" * V"IdRest"^0;
+ IdStart = alpha + P"_";
+ IdRest = alnum + P"_";
+
+ Number = (V"Hex" + V"Float" + V"Int") / tonumber;
+ Hex = (P"0x" + "0X") * expect(xdigit^1, "DigitHex");
+ Float = V"Decimal" * V"Expo"^-1
+ + V"Int" * V"Expo";
+ Decimal = digit^1 * "." * digit^0
+ + P"." * -P"." * expect(digit^1, "DigitDeci");
+ Expo = S"eE" * S"+-"^-1 * expect(digit^1, "DigitExpo");
+ Int = digit^1;
+
+ String = (V"ShortStr" + V"LongStr");
+ ShortStr = P'"' * Cs((V"EscSeq" + (P(1)-S'"\n'))^0) * expect(P'"', "Quote")
+ + P"'" * Cs((V"EscSeq" + (P(1)-S"'\n"))^0) * expect(P"'", "Quote");
+
+ EscSeq = P"\\" / "" -- remove backslash
+ * ( P"a" / "\a"
+ + P"b" / "\b"
+ + P"f" / "\f"
+ + P"n" / "\n"
+ + P"r" / "\r"
+ + P"t" / "\t"
+ + P"v" / "\v"
+
+ + P"\n" / "\n"
+ + P"\r" / "\n"
+
+ + P"\\" / "\\"
+ + P"\"" / "\""
+ + P"\'" / "\'"
+
+ + P"z" * space^0 / ""
+
+ + digit * digit^-2 / tonumber / string.char
+ + P"x" * expect(C(xdigit * xdigit), "HexEsc") * Cc(16) / tonumber / string.char
+ + P"u" * expect("{", "OBraceUEsc")
+ * expect(C(xdigit^1), "DigitUEsc") * Cc(16)
+ * expect("}", "CBraceUEsc")
+ / tonumber
+ / (utf8 and utf8.char or string.char) -- true max is \u{10FFFF}
+ -- utf8.char needs Lua 5.3
+ -- string.char works only until \u{FF}
+
+ + throw("EscSeq")
+ );
+
+ LongStr = V"Open" * C((P(1) - V"CloseEq")^0) * expect(V"Close", "CloseLStr") / function (s, eqs) return s end;
+ Open = "[" * Cg(V"Equals", "openEq") * "[" * P"\n"^-1;
+ Close = "]" * C(V"Equals") * "]";
+ Equals = P"="^0;
+ CloseEq = Cmt(V"Close" * Cb("openEq"), function (s, i, closeEq, openEq) return #openEq == #closeEq end);
+
+ OrOp = kw("or") / "or";
+ AndOp = kw("and") / "and";
+ RelOp = P("~=") / "ne"
+ + P("==") / "eq"
+ + P("<=") / "le"
+ + P(">=") / "ge"
+ + P("<") / "lt"
+ + P(">") / "gt";
+ BOrOp = P("|") / "bor";
+ BXorOp = P("~" * -P"=") / "bxor";
+ BAndOp = P("&") / "band";
+ ShiftOp = P("<<") / "shl"
+ + P(">>") / "shr";
+ ConcatOp = P("..") / "concat";
+ AddOp = P("+") / "add"
+ + P("-") / "sub";
+ MulOp = P("*") / "mul"
+ + P("//") / "idiv"
+ + P("/") / "div"
+ + P("%") / "mod";
+ UnaryOp = kw("not") / "not"
+ + P("-") / "unm"
+ + P("#") / "len"
+ + P("~") / "bnot";
+ PowOp = P("^") / "pow";
+}
+
+local parser = { detailed_errors = false }
+
+local validator = require("mkdoc.lua-parser.validator")
+local validate = validator.validate
+local syntaxerror = validator.syntaxerror
+
+function parser.parse (subject, filename, comments)
+ commentCollector = comments
+ local errorinfo = { subject = subject, filename = filename }
+ lpeg.setmaxstack(1000)
+ local ast, label, errorpos = lpeg.match(G, subject, nil, errorinfo)
+ if not ast then
+ if parser.detailed_errors then
+ local re = require "relabel"
+ local line, col = re.calcline(subject, errorpos)
+ return ast, {
+ line = line;
+ column = col;
+ id = labels[label][1];
+ message = labels[label][2];
+ position = errorpos;
+ }
+ else
+ local errmsg = labels[label][2]
+ return ast, syntaxerror(errorinfo, errorpos, errmsg)
+ end
+ end
+ commentCollector = nil
+ return validate(ast, errorinfo)
+end
+
+return parser
diff --git a/src/mkdoc/lua-parser/scope.lua b/src/mkdoc/lua-parser/scope.lua
new file mode 100644
index 0000000..dd19392
--- /dev/null
+++ b/src/mkdoc/lua-parser/scope.lua
@@ -0,0 +1,74 @@
+--[[
+This module implements functions that handle scoping rules
+]]
+local scope = {}
+
+function scope.lineno (s, i)
+ if i == 1 then return 1, 1 end
+ local l, lastline = 0, ""
+ s = s:sub(1, i) .. "\n"
+ for line in s:gmatch("[^\n]*[\n]") do
+ l = l + 1
+ lastline = line
+ end
+ local c = lastline:len() - 1
+ return l, c ~= 0 and c or 1
+end
+
+function scope.new_scope (env)
+ if not env.scope then
+ env.scope = 0
+ else
+ env.scope = env.scope + 1
+ end
+ local scope = env.scope
+ env.maxscope = scope
+ env[scope] = {}
+ env[scope]["label"] = {}
+ env[scope]["local"] = {}
+ env[scope]["goto"] = {}
+end
+
+function scope.begin_scope (env)
+ env.scope = env.scope + 1
+end
+
+function scope.end_scope (env)
+ env.scope = env.scope - 1
+end
+
+function scope.new_function (env)
+ if not env.fscope then
+ env.fscope = 0
+ else
+ env.fscope = env.fscope + 1
+ end
+ local fscope = env.fscope
+ env["function"][fscope] = {}
+end
+
+function scope.begin_function (env)
+ env.fscope = env.fscope + 1
+end
+
+function scope.end_function (env)
+ env.fscope = env.fscope - 1
+end
+
+function scope.begin_loop (env)
+ if not env.loop then
+ env.loop = 1
+ else
+ env.loop = env.loop + 1
+ end
+end
+
+function scope.end_loop (env)
+ env.loop = env.loop - 1
+end
+
+function scope.insideloop (env)
+ return env.loop and env.loop > 0
+end
+
+return scope
diff --git a/src/mkdoc/lua-parser/validator.lua b/src/mkdoc/lua-parser/validator.lua
new file mode 100644
index 0000000..41a86d7
--- /dev/null
+++ b/src/mkdoc/lua-parser/validator.lua
@@ -0,0 +1,394 @@
+--[[
+This module impements a validator for the AST
+]]
+local scope = require "mkdoc.lua-parser.scope"
+
+local lineno = scope.lineno
+local new_scope, end_scope = scope.new_scope, scope.end_scope
+local new_function, end_function = scope.new_function, scope.end_function
+local begin_loop, end_loop = scope.begin_loop, scope.end_loop
+local insideloop = scope.insideloop
+
+-- creates an error message for the input string
+local function syntaxerror (errorinfo, pos, msg)
+ local l, c = lineno(errorinfo.subject, pos)
+ local error_msg = "%s:%d:%d: syntax error, %s"
+ return string.format(error_msg, errorinfo.filename, l, c, msg)
+end
+
+local function exist_label (env, scope, stm)
+ local l = stm[1]
+ for s=scope, 0, -1 do
+ if env[s]["label"][l] then return true end
+ end
+ return false
+end
+
+local function set_label (env, label, pos)
+ local scope = env.scope
+ local l = env[scope]["label"][label]
+ if not l then
+ env[scope]["label"][label] = { name = label, pos = pos }
+ return true
+ else
+ local msg = "label '%s' already defined at line %d"
+ local line = lineno(env.errorinfo.subject, l.pos)
+ msg = string.format(msg, label, line)
+ return nil, syntaxerror(env.errorinfo, pos, msg)
+ end
+end
+
+local function set_pending_goto (env, stm)
+ local scope = env.scope
+ table.insert(env[scope]["goto"], stm)
+ return true
+end
+
+local function verify_pending_gotos (env)
+ for s=env.maxscope, 0, -1 do
+ for k, v in ipairs(env[s]["goto"]) do
+ if not exist_label(env, s, v) then
+ local msg = "no visible label '%s' for "
+ msg = string.format(msg, v[1])
+ return nil, syntaxerror(env.errorinfo, v.pos, msg)
+ end
+ end
+ end
+ return true
+end
+
+local function set_vararg (env, is_vararg)
+ env["function"][env.fscope].is_vararg = is_vararg
+end
+
+local traverse_stm, traverse_exp, traverse_var
+local traverse_block, traverse_explist, traverse_varlist, traverse_parlist
+
+function traverse_parlist (env, parlist)
+ local len = #parlist
+ local is_vararg = false
+ if len > 0 and parlist[len].tag == "Dots" then
+ is_vararg = true
+ end
+ set_vararg(env, is_vararg)
+ return true
+end
+
+local function traverse_function (env, exp)
+ new_function(env)
+ new_scope(env)
+ local status, msg = traverse_parlist(env, exp[1])
+ if not status then return status, msg end
+ status, msg = traverse_block(env, exp[2])
+ if not status then return status, msg end
+ end_scope(env)
+ end_function(env)
+ return true
+end
+
+local function traverse_op (env, exp)
+ local status, msg = traverse_exp(env, exp[2])
+ if not status then return status, msg end
+ if exp[3] then
+ status, msg = traverse_exp(env, exp[3])
+ if not status then return status, msg end
+ end
+ return true
+end
+
+local function traverse_paren (env, exp)
+ local status, msg = traverse_exp(env, exp[1])
+ if not status then return status, msg end
+ return true
+end
+
+local function traverse_table (env, fieldlist)
+ for k, v in ipairs(fieldlist) do
+ local tag = v.tag
+ if tag == "Pair" then
+ local status, msg = traverse_exp(env, v[1])
+ if not status then return status, msg end
+ status, msg = traverse_exp(env, v[2])
+ if not status then return status, msg end
+ else
+ local status, msg = traverse_exp(env, v)
+ if not status then return status, msg end
+ end
+ end
+ return true
+end
+
+local function traverse_vararg (env, exp)
+ if not env["function"][env.fscope].is_vararg then
+ local msg = "cannot use '...' outside a vararg function"
+ return nil, syntaxerror(env.errorinfo, exp.pos, msg)
+ end
+ return true
+end
+
+local function traverse_call (env, call)
+ local status, msg = traverse_exp(env, call[1])
+ if not status then return status, msg end
+ for i=2, #call do
+ status, msg = traverse_exp(env, call[i])
+ if not status then return status, msg end
+ end
+ return true
+end
+
+local function traverse_invoke (env, invoke)
+ local status, msg = traverse_exp(env, invoke[1])
+ if not status then return status, msg end
+ for i=3, #invoke do
+ status, msg = traverse_exp(env, invoke[i])
+ if not status then return status, msg end
+ end
+ return true
+end
+
+local function traverse_assignment (env, stm)
+ local status, msg = traverse_varlist(env, stm[1])
+ if not status then return status, msg end
+ status, msg = traverse_explist(env, stm[2])
+ if not status then return status, msg end
+ return true
+end
+
+local function traverse_break (env, stm)
+ if not insideloop(env) then
+ local msg = " not inside a loop"
+ return nil, syntaxerror(env.errorinfo, stm.pos, msg)
+ end
+ return true
+end
+
+local function traverse_forin (env, stm)
+ begin_loop(env)
+ new_scope(env)
+ local status, msg = traverse_explist(env, stm[2])
+ if not status then return status, msg end
+ status, msg = traverse_block(env, stm[3])
+ if not status then return status, msg end
+ end_scope(env)
+ end_loop(env)
+ return true
+end
+
+local function traverse_fornum (env, stm)
+ local status, msg
+ begin_loop(env)
+ new_scope(env)
+ status, msg = traverse_exp(env, stm[2])
+ if not status then return status, msg end
+ status, msg = traverse_exp(env, stm[3])
+ if not status then return status, msg end
+ if stm[5] then
+ status, msg = traverse_exp(env, stm[4])
+ if not status then return status, msg end
+ status, msg = traverse_block(env, stm[5])
+ if not status then return status, msg end
+ else
+ status, msg = traverse_block(env, stm[4])
+ if not status then return status, msg end
+ end
+ end_scope(env)
+ end_loop(env)
+ return true
+end
+
+local function traverse_goto (env, stm)
+ local status, msg = set_pending_goto(env, stm)
+ if not status then return status, msg end
+ return true
+end
+
+local function traverse_if (env, stm)
+ local len = #stm
+ if len % 2 == 0 then
+ for i=1, len, 2 do
+ local status, msg = traverse_exp(env, stm[i])
+ if not status then return status, msg end
+ status, msg = traverse_block(env, stm[i+1])
+ if not status then return status, msg end
+ end
+ else
+ for i=1, len-1, 2 do
+ local status, msg = traverse_exp(env, stm[i])
+ if not status then return status, msg end
+ status, msg = traverse_block(env, stm[i+1])
+ if not status then return status, msg end
+ end
+ local status, msg = traverse_block(env, stm[len])
+ if not status then return status, msg end
+ end
+ return true
+end
+
+local function traverse_label (env, stm)
+ local status, msg = set_label(env, stm[1], stm.pos)
+ if not status then return status, msg end
+ return true
+end
+
+local function traverse_let (env, stm)
+ local status, msg = traverse_explist(env, stm[2])
+ if not status then return status, msg end
+ return true
+end
+
+local function traverse_letrec (env, stm)
+ local status, msg = traverse_exp(env, stm[2][1])
+ if not status then return status, msg end
+ return true
+end
+
+local function traverse_repeat (env, stm)
+ begin_loop(env)
+ local status, msg = traverse_block(env, stm[1])
+ if not status then return status, msg end
+ status, msg = traverse_exp(env, stm[2])
+ if not status then return status, msg end
+ end_loop(env)
+ return true
+end
+
+local function traverse_return (env, stm)
+ local status, msg = traverse_explist(env, stm)
+ if not status then return status, msg end
+ return true
+end
+
+local function traverse_while (env, stm)
+ begin_loop(env)
+ local status, msg = traverse_exp(env, stm[1])
+ if not status then return status, msg end
+ status, msg = traverse_block(env, stm[2])
+ if not status then return status, msg end
+ end_loop(env)
+ return true
+end
+
+function traverse_var (env, var)
+ local tag = var.tag
+ if tag == "Id" then -- `Id{ }
+ return true
+ elseif tag == "Index" then -- `Index{ expr expr }
+ local status, msg = traverse_exp(env, var[1])
+ if not status then return status, msg end
+ status, msg = traverse_exp(env, var[2])
+ if not status then return status, msg end
+ return true
+ else
+ error("expecting a variable, but got a " .. tag)
+ end
+end
+
+function traverse_varlist (env, varlist)
+ for k, v in ipairs(varlist) do
+ local status, msg = traverse_var(env, v)
+ if not status then return status, msg end
+ end
+ return true
+end
+
+function traverse_exp (env, exp)
+ local tag = exp.tag
+ if tag == "Nil" or
+ tag == "Boolean" or -- `Boolean{ }
+ tag == "Number" or -- `Number{ }
+ tag == "String" then -- `String{ }
+ return true
+ elseif tag == "Dots" then
+ return traverse_vararg(env, exp)
+ elseif tag == "Function" then -- `Function{ { `Id{ }* `Dots? } block }
+ return traverse_function(env, exp)
+ elseif tag == "Table" then -- `Table{ ( `Pair{ expr expr } | expr )* }
+ return traverse_table(env, exp)
+ elseif tag == "Op" then -- `Op{ opid expr expr? }
+ return traverse_op(env, exp)
+ elseif tag == "Paren" then -- `Paren{ expr }
+ return traverse_paren(env, exp)
+ elseif tag == "Call" then -- `Call{ expr expr* }
+ return traverse_call(env, exp)
+ elseif tag == "Invoke" then -- `Invoke{ expr `String{ } expr* }
+ return traverse_invoke(env, exp)
+ elseif tag == "Id" or -- `Id{ }
+ tag == "Index" then -- `Index{ expr expr }
+ return traverse_var(env, exp)
+ else
+ error("expecting an expression, but got a " .. tag)
+ end
+end
+
+function traverse_explist (env, explist)
+ for k, v in ipairs(explist) do
+ local status, msg = traverse_exp(env, v)
+ if not status then return status, msg end
+ end
+ return true
+end
+
+function traverse_stm (env, stm)
+ local tag = stm.tag
+ if tag == "Do" then -- `Do{ stat* }
+ return traverse_block(env, stm)
+ elseif tag == "Set" then -- `Set{ {lhs+} {expr+} }
+ return traverse_assignment(env, stm)
+ elseif tag == "While" then -- `While{ expr block }
+ return traverse_while(env, stm)
+ elseif tag == "Repeat" then -- `Repeat{ block expr }
+ return traverse_repeat(env, stm)
+ elseif tag == "If" then -- `If{ (expr block)+ block? }
+ return traverse_if(env, stm)
+ elseif tag == "Fornum" then -- `Fornum{ ident expr expr expr? block }
+ return traverse_fornum(env, stm)
+ elseif tag == "Forin" then -- `Forin{ {ident+} {expr+} block }
+ return traverse_forin(env, stm)
+ elseif tag == "Local" then -- `Local{ {ident+} {expr+}? }
+ return traverse_let(env, stm)
+ elseif tag == "Localrec" then -- `Localrec{ ident expr }
+ return traverse_letrec(env, stm)
+ elseif tag == "Goto" then -- `Goto{ }
+ return traverse_goto(env, stm)
+ elseif tag == "Label" then -- `Label{ }
+ return traverse_label(env, stm)
+ elseif tag == "Return" then -- `Return{ * }
+ return traverse_return(env, stm)
+ elseif tag == "Break" then
+ return traverse_break(env, stm)
+ elseif tag == "Call" then -- `Call{ expr expr* }
+ return traverse_call(env, stm)
+ elseif tag == "Invoke" then -- `Invoke{ expr `String{ } expr* }
+ return traverse_invoke(env, stm)
+ else
+ error("expecting a statement, but got a " .. tag)
+ end
+end
+
+function traverse_block (env, block)
+ local l = {}
+ new_scope(env)
+ for k, v in ipairs(block) do
+ local status, msg = traverse_stm(env, v)
+ if not status then return status, msg end
+ end
+ end_scope(env)
+ return true
+end
+
+
+local function traverse (ast, errorinfo)
+ assert(type(ast) == "table")
+ assert(type(errorinfo) == "table")
+ local env = { errorinfo = errorinfo, ["function"] = {} }
+ new_function(env)
+ set_vararg(env, true)
+ local status, msg = traverse_block(env, ast)
+ if not status then return status, msg end
+ end_function(env)
+ status, msg = verify_pending_gotos(env)
+ if not status then return status, msg end
+ return ast
+end
+
+return { validate = traverse, syntaxerror = syntaxerror }
diff --git a/src/mkdoc/main.lua b/src/mkdoc/main.lua
new file mode 100644
index 0000000..fa188ed
--- /dev/null
+++ b/src/mkdoc/main.lua
@@ -0,0 +1,1260 @@
+os.setlocale("C")
+
+local path = require("path") -- https://luarocks.org/modules/xavier-wang/lpath
+local fs = require("path.fs") -- https://luarocks.org/modules/xavier-wang/lpath
+local perf = require("perf")
+
+local format = string.format
+
+---------------------------------------------------------------------------------------------------------------------------
+
+local function fprintf(file, ...)
+ file:write(format(...))
+end
+
+local function printf(...)
+ io.write(format(...))
+end
+
+local function append(t, v)
+ t[#t + 1] = v
+end
+
+local function addSet(t, v)
+ assert(not t[v])
+ t[v] = true
+ t[#t + 1] = v
+end
+
+local function add(t, k, v)
+ assert(not t[k], k)
+ assert(not t[v])
+ t[k] = v
+ t[v] = true
+ t[#t + 1] = v
+end
+
+local function addr(t, k, v, r)
+ assert(not t[k])
+ assert(not t[v])
+ assert(not t[r])
+ t[k] = v
+ t[v] = true
+ t[r] = v
+ t[#t + 1] = v
+end
+
+---------------------------------------------------------------------------------------------------------------------------
+
+local ARGS = { ... }
+local TRACE = (ARGS[1] == "trace") or false
+
+fs.removedirs("../doc/gen")
+fs.makedirs("../doc/gen/lwtk")
+
+---------------------------------------------------------------------------------------------------------------------------
+
+perf.start"directoryScan"
+
+local moduleFileNames = {}
+for n, t in fs.scandir("lwtk") do
+ if t == "file" and path.isfile(n) then
+ moduleFileNames[#moduleFileNames + 1] = n
+ end
+end
+
+perf.stop"directoryScan"
+
+---------------------------------------------------------------------------------------------------------------------------
+
+perf.start"requireModules"
+
+local reverseLookup = {}
+local isPackage = {}
+local toModuleFileName = {}
+local packageInfos = {}
+local moduleInfos = {}
+local classInfos = {}
+local mixinInfos = {}
+local metaInfos = {}
+local functionInfos = {}
+local otherInfos = {}
+local classMixinInfos = {}
+local depOrderedModuleNames = {}
+local modules = {}
+
+---------------------------------------------------------------------------------------------------------------------------
+
+local original_require = _G.require
+do
+ local moduleNames = {}
+
+ for i, moduleFileName in ipairs(moduleFileNames) do
+ local moduleName = moduleFileName:gsub("^(.*)%.lua$", "%1"):gsub("/", "."):gsub("%.init$", "")
+ isPackage[moduleName] = moduleFileName:match("%/init%.lua$") and true or false
+ moduleNames[i] = moduleName
+ toModuleFileName[moduleName] = moduleFileName
+ end
+
+ function _G.require(moduleName)
+ local module = original_require(moduleName)
+ if not moduleName:match("^lwtk") then
+ return module
+ end
+ if not reverseLookup[module] then
+ --printf("Loading %s\n", moduleName, )
+ append(depOrderedModuleNames, moduleName)
+ local isP = isPackage[moduleName]
+ local moduleType = type(module)
+ local packageName = moduleName:gsub("^(.*)%.[^.]+$", "%1")
+ local isInternal = moduleName:match("^lwtk%.internal") and true or false
+ local moduleInfo = {
+ moduleName = moduleName,
+ docFileName = toModuleFileName[moduleName]:gsub("%.lua", ".md"),
+ packageName = packageName,
+ isInternal = isInternal,
+ isModule = true,
+ isPackage = isP,
+ moduleType = moduleType,
+ memberInfos = {},
+ referringInfos = {}
+ }
+ reverseLookup[module] = moduleInfo
+ addr(moduleInfos, moduleName, moduleInfo, module)
+ if isP then
+ add(packageInfos, moduleName, moduleInfo)
+ end
+ modules[moduleName] = module
+ end
+ return module
+ end
+
+ for _, moduleName in ipairs(moduleNames) do
+ --printf("Requiring %s\n", moduleName)
+ require(moduleName)
+ end
+end
+_G.require = original_require
+
+perf.stop"requireModules"
+
+---------------------------------------------------------------------------------------------------------------------------
+
+local lpeg = require "lpeglabel" -- https://luarocks.org/modules/sergio-medeiros/lpeglabel
+local re = require("relabel") -- https://luarocks.org/modules/sergio-medeiros/lpeglabel
+
+local pprint = require("mkdoc.pprint") -- modified version from: https://github.com/jagt/pprint.lua
+local parser = require("mkdoc.lua-parser.parser") -- modified version from: https://github.com/andremm/lua-parser
+local comments = require("mkdoc.comments")
+local util = require("mkdoc.util")
+
+local lwtk = require("lwtk")
+local getMixinBase = lwtk.get.mixinBase
+
+---------------------------------------------------------------------------------------------------------------------------
+
+local getClassModuleName = util.getClassModuleName
+
+local function getClassModuleInfo(c)
+ return moduleInfos[getClassModuleName(c)]
+end
+
+---------------------------------------------------------------------------------------------------------------------------
+
+
+lpeg.locale(lpeg)
+
+local P, S, V = lpeg.P, lpeg.S, lpeg.V
+local C, Carg, Cb, Cc = lpeg.C, lpeg.Carg, lpeg.Cb, lpeg.Cc
+local Cf, Cg, Cmt, Cp, Cs, Ct = lpeg.Cf, lpeg.Cg, lpeg.Cmt, lpeg.Cp, lpeg.Cs, lpeg.Ct
+local Lc, T = lpeg.Lc, lpeg.T
+
+local alpha, digit, alnum = lpeg.alpha, lpeg.digit, lpeg.alnum
+local xdigit = lpeg.xdigit
+local space = lpeg.space
+
+---------------------------------------------------------------------------------------------------------------------------
+
+perf.start"moduleScan"
+
+do
+ local function addMemberInfo(moduleInfo, memberName, member, class, parentInfo)
+ -- for Mixins: class != module
+ local moduleName = moduleInfo.moduleName
+ local memberType = type(member)
+ if memberType == "table" or memberType == "function" then
+ local memberInfo = moduleInfo.memberInfos[memberName]
+ local declared = class and class.declared[memberName] or moduleInfo.isMixin
+ if not memberInfo then
+ local memberInfo = {
+ moduleName = moduleName,
+ isModuleMember = true,
+ isModuleSubMember = (parentInfo ~= nil and true or nil),
+ isEntity = true,
+ memberName = memberName,
+ memberType = memberType,
+ member = member,
+ referringInfos = {},
+ declared = declared
+ }
+ if memberType == "function" then
+ local isObjMember = class and rawget(class.__index, memberName)
+ if declared and (not class or isObjMember) then
+ addr(moduleInfo.memberFunctions, memberName, memberInfo, member)
+ elseif class and isObjMember then
+ local inheritedMethods = moduleInfo.inheritedMethods -- TODO mixin
+ local i = 0
+ local s = class:getSuperClass()
+ while s do
+ i = i + 1
+ local fromList = inheritedMethods[i]
+ if not fromList then
+ fromList = { moduleName = getClassModuleName(s), isMixin = getMixinBase[s] }
+ inheritedMethods[i] = fromList
+ end
+ if s.declared[memberName] then
+ append(fromList, { moduleName = getClassModuleName(s),
+ memberName = memberName })
+ break
+ end
+ s = s:getSuperClass()
+ end
+ end
+ end
+ local existingInfo = reverseLookup[member]
+ if not existingInfo then
+ reverseLookup[member] = memberInfo
+ else
+ if existingInfo.moduleName == moduleName then
+ error(require"inspect"{memberName, moduleName, existingInfo})
+ end
+ add(existingInfo.referringInfos, moduleName, memberInfo)
+ memberInfo.originalMemberInfo = existingInfo
+ end
+ addr(moduleInfo.memberInfos, memberName, memberInfo, member)
+
+ if memberType == "table" and not memberName:match("^__") and memberName ~= "implement"
+ and memberName ~= "override" and memberName ~= "declared" and memberName ~= "static"
+ and not getmetatable(member)
+ then
+ --print("####", memberName)
+ local mt = getmetatable(member)
+ local _pairs = mt and mt.__pairs or pairs
+ for k, v in _pairs(member) do
+ --assert(type(k) == "string", require"inspect"{moduleName, memberName, k, v})
+ addMemberInfo(moduleInfo, memberName.."."..k, v, nil, memberInfo)
+ end
+ end
+ else
+ assert(moduleInfo.isMixin)
+ if memberType == "function" then
+ if declared then
+ if moduleInfo.memberFunctions[memberName] ~= memberInfo then
+ error(require"inspect"{moduleInfo.moduleName, memberName, memberInfo})
+ end
+ elseif class then
+ assert(moduleInfo.memberFunctions[memberName] == nil)
+ end
+ end
+ if memberType ~= memberInfo.memberType then
+ if memberInfo.memberType == "boolean" and memberInfo.member == false then
+ assert(memberType == "table" or memberType == "function")
+ memberInfo.memberType = memberType
+ memberInfo.isEntity = true
+ memberInfo.member = nil
+ memberInfo.memberRealisation = { member }
+ else
+ error(require"inspect"{moduleName, memberName, memberType, memberInfo.memberType, memberInfo})
+ end
+ end
+ end
+ else
+ local memberInfo = moduleInfo.memberInfos[memberName]
+ if not memberInfo then
+ local memberInfo = {
+ moduleName = moduleName,
+ isModuleMember = true,
+ isValue = true,
+ memberName = memberName,
+ memberType = memberType,
+ member = member
+ }
+ add(moduleInfo.memberInfos, memberName, memberInfo)
+ else
+ assert(moduleInfo.isMixin)
+ if memberType ~= memberInfo.memberType or member ~= memberInfo.member then
+ error(require"inspect"{moduleName, memberName, memberType, memberInfo.memberType, memberInfo})
+ end
+ end
+ end
+ end
+
+ local function visitSuperClasses(thisModuleName, thisModule, thisModuleInfo)
+ local thisClassPath = util.getReverseClassPathString(thisModule)
+ local super = thisModule:getSuperClass()
+ local mixinVisited = false
+ while super do
+ local superInfo = getClassModuleInfo(super)
+ addSet(superInfo.subClassPaths, thisClassPath)
+ if not mixinVisited and superInfo.isMixin then
+ -- for Mixins: class != module, i.e. super != modules[superName]
+ if superInfo.mixinRealisations[super] then
+ mixinVisited = true
+ else
+ local list = util.getReverseClassPathList(super)
+ list.pathString = util.classPathListToString(list)
+ addr(superInfo.mixinRealisations, super, list, list.pathString)
+ for memberName, member in pairs(super.__index) do
+ if super.declared[memberName] then
+ addMemberInfo(superInfo, memberName, member, super)
+ end
+ end
+ end
+ end
+ super = super:getSuperClass()
+ end
+ end
+
+ for _, thisModuleName in ipairs(depOrderedModuleNames) do
+ local thisModule = modules[thisModuleName]
+ local thisModuleInfo = moduleInfos[thisModuleName]
+ local thisModuleType = type(thisModule)
+
+ local isClass = lwtk.isInstanceOf(thisModule, lwtk.Class)
+ local isMixin = lwtk.isInstanceOf(thisModule, lwtk.Mixin)
+ local isMeta = lwtk.isInstanceOf(thisModule, lwtk.Meta)
+ local isFunction = (thisModuleType == "function")
+ if isClass then
+ thisModuleInfo.isClass = true
+ thisModuleInfo.memberFunctions = {}
+ thisModuleInfo.inheritedMethods = {}
+ elseif isMixin then
+ thisModuleInfo.isMixin = true
+ thisModuleInfo.memberFunctions = {}
+ thisModuleInfo.inheritedMethods = {}
+ elseif isMeta then
+ thisModuleInfo.isMeta = true
+ elseif isFunction then
+ thisModuleInfo.isFunction = true
+ end
+
+ if thisModuleType == "table" and not thisModuleInfo.isPackage then
+ local mt = getmetatable(thisModule)
+ local _pairs = mt and mt.__pairs or pairs
+ local class = isClass and thisModule
+ for memberName, member in _pairs(thisModule) do
+ addMemberInfo(thisModuleInfo, memberName, member, class)
+ end
+ end
+
+ if isClass --[[or isMixin]] then
+ thisModuleInfo.subClassPaths = {}
+ elseif isMixin then
+ thisModuleInfo.subClassPaths = {}
+ thisModuleInfo.mixinRealisations = {}
+ end
+ if not thisModuleInfo.isInternal then
+ if isClass then
+ add(classInfos, thisModuleName, thisModuleInfo)
+ add(classMixinInfos, thisModuleName, thisModuleInfo)
+ visitSuperClasses(thisModuleName, thisModule, thisModuleInfo)
+ elseif isMixin then
+ add(mixinInfos, thisModuleName, thisModuleInfo)
+ add(classMixinInfos, thisModuleName, thisModuleInfo)
+ elseif isMeta then
+ add(metaInfos, thisModuleName, thisModuleInfo)
+ elseif isFunction then
+ add(functionInfos, thisModuleName, thisModuleInfo)
+ else
+ add(otherInfos, thisModuleName, thisModuleInfo)
+ end
+ end
+ end
+end
+do
+ for _, thisModuleInfo in ipairs(classInfos) do
+ local thisModuleName = thisModuleInfo.moduleName
+ local thisModule = modules[thisModuleName]
+ for k, v in pairs(thisModule.__index) do
+ local memberInfo = thisModuleInfo.memberInfos[k]
+ if not memberInfo then
+ error(require"inspect"{thisModuleName, k})
+ end
+ if type(v) == "function" then
+ if thisModule.declared[k] then
+ memberInfo.overrideList = util.getOverrideList(thisModule, k) -- TODO
+ end
+ end
+ end
+ end
+end
+perf.stop"moduleScan"
+
+
+---------------------------------------------------------------------------------------------------------------------------
+
+perf.start"totalparse"
+
+local parseModule
+do
+ local function processExp(env, e)
+ if e.tag == "Call" then
+ local func = processExp(env, e[1])
+ local args = {}
+ for i = 2, #e do
+ args[i-1] = processExp(env, e[i])
+ end
+ if func == require then
+ --local rslt = require(args[1])
+ local rslt = moduleInfos[args[1]]
+ if not rslt then
+ rslt = require(args[1])
+ end
+ return rslt
+ elseif func == lwtk.newClass then
+ --[[
+ local class = lwtk.tryrequire(args[1])
+ if class then
+ if classDocs[class] ~= nil then
+ lwtk.errorf("already found: lwtk.newClass(%q)", class.__name)
+ end
+ classDocs[class] = false
+ return class, NEW_CLASS
+ else
+ print("ignoring class", args[1])
+ end ]]
+ end
+ elseif e.tag == "Index" then
+ local e1 = processExp(env, e[1])
+ local e2 = processExp(env, e[2])
+ if e1 then
+ if moduleInfos[e1] == true then
+ return e1.memberInfos[e2]
+ else
+ return e1[e2]
+ end
+ end
+ elseif e.tag == "String" then
+ return e[1]
+ elseif e.tag == "Op" then
+ local op = e[1]
+ if op == "concat" then
+ local e1 = processExp(env, e[2])
+ local e2 = processExp(env, e[3])
+ return e1..e2
+ elseif op == "or" then
+ local e1 = processExp(env, e[2])
+ local e2 = processExp(env, e[3])
+ return e1 or e2
+ elseif op == "div" then
+ elseif op == "not" then
+ elseif op == "and" then
+ else
+ error(op)
+ end
+ elseif e.tag == "Id" then
+ return env[e[1]]
+ elseif e.tag == "Number" then
+ return e[1]
+ elseif e.tag == "Table" then
+ elseif e.tag == "Function" then
+ elseif e.tag == "Boolean" then
+ return e[1]
+ elseif e.tag == "Invoke" then
+ elseif e.tag == "Paren" then
+ elseif e.tag == "Nil" then
+ return nil
+ else
+ error(require"inspect"({tag = e.tag, pos = e.pos, end_pos = e.end_pos}))
+ end
+ end
+
+ local moduleOutDirs = {}
+
+ function parseModule(thisModuleName, thisModule, thisModuleInfo)
+ local moduleFileNameStem = thisModuleName:gsub("%.", "/")
+ if thisModuleInfo.isPackage then
+ moduleFileNameStem = moduleFileNameStem.."/init"
+ end
+ local moduleFileName = moduleFileNameStem..".lua"
+ local traceFileName = path("../doc/gen", moduleFileNameStem..".trace")
+ local outDir = path.parent(traceFileName)
+ if not moduleOutDirs[outDir] then
+ moduleOutDirs[outDir] = true
+ fs.makedirs(outDir)
+ end
+
+ local traceOut = TRACE and io.open(traceFileName, "w")
+ do
+ print("Processing", moduleFileName)
+ local function trace(...)
+ if traceOut then
+ local n = select("#", ...)
+ for i = 1, n do
+ traceOut:write(tostring(select(i, ...)))
+ if i < n then
+ traceOut:write("\t")
+ end
+ end
+ traceOut:write("\n")
+ end
+ end
+
+ local file = assert(io.open(moduleFileName, "r"), moduleFileName)
+ local content = file:read("*a")
+ file:close()
+
+ comments.init(content, trace)
+ perf.start"parse"
+ local ast, err = parser.parse(content, moduleFileName, comments)
+ perf.stop"parse"
+ comments.sort()
+
+ ------------------------------------------------------------------------------------
+ if TRACE then
+ perf.start"trace"
+ trace(pprint.pformat(ast))
+ perf.stop"trace"
+ end
+ ------------------------------------------------------------------------------------
+
+ local returnId = nil
+ local isClass = thisModuleInfo.isClass
+ local isMeta = thisModuleInfo.isMeta
+ local isMixin = thisModuleInfo.isMixin
+ local isFunction = thisModuleInfo.isFunction
+
+ local last = ast[#ast]
+ assert(last.tag == "Return" and #last == 1)
+ if last[1].tag == "Id" then
+ returnId = last[1][1]
+ else
+ local s = last
+ local doc, argListDoc = comments.stripArgListDoc(comments.getBefore(s.pos))
+ local shortDoc = comments.getShortDoc(doc)
+ thisModuleInfo.fullDoc = doc
+ thisModuleInfo.shortDoc = shortDoc
+ local argListString, isMethod = util.getArgListString(s[1])
+ if argListString and argListDoc then
+ assert(argListString == argListDoc)
+ end
+ if not argListString and argListDoc then
+ argListString, isMethod = comments.argListDocToMethod(argListDoc)
+ end
+ --assert(not isMethod)
+ thisModuleInfo.argListString = argListString
+ thisModuleInfo.isMethod = isMethod
+ end
+ local function processBlock(ast, env)
+ for _, s in ipairs(ast) do
+ trace("TTT", s.pos, s.tag)
+ local tag = s.tag
+ if tag == "Local" or tag == "Localrec" then
+ local s1 = s[1];
+ local s2 = s[2];
+ local isReturnId = false
+ if s.tag == "Local" then
+ assert(s1.tag == "NameList")
+ assert(not s2.tag or s2.tag == "ExpList", s2)
+ for i = 1, #s1 do
+ local n = s1[i]; assert(n.tag == "Id")
+ if n[1] == returnId then
+ assert(#s1 == 1 and #s2 == 1)
+ isReturnId = true
+ break
+ end
+ end
+ else
+ assert(#s1 == 1 and s1.tag == nil and s1[1].tag == "Id")
+ isReturnId = (s1[1][1] == returnId)
+ end
+ if isReturnId then
+ trace("NEW_MODULE", s.pos, s.end_pos)
+ local docb = comments.getBefore(s.pos)
+ local doca = comments.getAfter(s.end_pos)
+ if docb then
+ trace("FOUNDBEFORE", docb)--require"inspect"(doc))
+ end
+ if doca then
+ trace("FOUNDAFTER", doca)
+ end
+ if doca and docb then
+ lwtk.errorf("Ambiguous class comment in line %d of file %q", re.calcline(content, s.pos), moduleFileName)
+ end
+ env[returnId] = thisModule
+ local doc, argListDoc = comments.stripArgListDoc(docb or doca)
+ local shortDoc = comments.getShortDoc(doc)
+ thisModuleInfo.fullDoc = doc
+ thisModuleInfo.shortDoc = shortDoc
+
+ if isFunction then
+ local argListString, isMethod
+ if tag == "Localrec" then
+ argListString, isMethod = util.getArgListString(s2[1])
+ --assert(not isMethod)
+ end
+ if not argListString and argListDoc then
+ argListString, isMethod = comments.argListDocToMethod(argListDoc)
+ end
+ thisModuleInfo.argListString = argListString
+ thisModuleInfo.isMethod = isMethod
+ end
+ elseif tag == "Localrec" then
+ assert(#s2 == 1 and s2.tag == nil and s2[1].tag == "Function")
+ local rslt = processExp(env, s2[1])
+ trace("setting", s1[1][1], rslt)
+ env[s1[1][1]] = rslt
+ else
+ for i = 1, #s1 do
+ local n = s1[i]; assert(n.tag == "Id")
+ local e = s2[i]
+ if e then
+ local rslt = processExp(env, e)
+ trace("setting", n[1], rslt)
+ env[n[1]] = rslt
+ end
+ end
+ end
+ elseif tag == "Set" then
+ local lhsl = s[1];
+ local expl = s[2];
+ local memberInfo = nil
+ for i = 1, #lhsl do
+ local lhs = processExp(env, lhsl[i]);
+ memberInfo = thisModuleInfo.memberInfos[lhs]
+ if memberInfo then
+ assert(#lhsl == 1)
+ break
+ end
+ end
+ if memberInfo then
+ local exp = expl[1]
+ local argListString, isMethod = util.getArgListString(exp)
+ local fullDoc, argListDoc = comments.stripArgListDoc(comments.getBefore(s.pos))
+ if not argListString and argListDoc then
+ argListString, isMethod = comments.argListDocToMethod(argListDoc)
+ end
+ memberInfo.fullDoc = fullDoc
+ memberInfo.shortDoc = comments.getShortDoc(fullDoc)
+ memberInfo.argListString = argListString
+ memberInfo.isMethod = isMethod
+ --trace("LLLLLLLLLLLL", require"inspect"{memberInfo.memberName, processExp(env, exp), lwtk.TextLabel.getMeasures })
+ end
+ elseif tag == "Invoke" then
+ local e1 = processExp(env, s[1])
+ local m = processExp(env, s[2])
+ if isClass and e1 == thisModule then
+ trace("CCCCCCCCCCCCCCCCCCCC", e1)
+ if m == "declare" then
+ for i = 3, #s do
+ local arg = processExp(env, s[i])
+ trace("DDDDDDDDDDDDDDDDD", arg)
+ assert(type(arg) == "string")
+ --declared[arg] = declDoc
+ local nextPos
+ if i < #s then
+ nextPos = s[i + 1].pos
+ else
+ nextPos = s.end_pos
+ end
+ local doca = comments.getAfter(s[i].end_pos, nextPos)
+ local docb = comments.getBefore(s[i].pos)
+ if doca then
+ trace("FOUNDAFTER", doca)
+ end
+ if docb then
+ trace("FOUNDBEFORE", docb)
+ end
+ if doca and docb then
+ lwtk.errorf("Ambiguous comment for declaration in line %d of file %q", re.calcline(content, s[i].pos), moduleFileName)
+ end
+ end
+ end
+ end
+ elseif tag == "Block" then
+ local nextEnv = setmetatable({}, { __index = env })
+ processBlock(s, nextEnv)
+ elseif tag == "If" then
+ local cond = processExp(env, s[1])
+ local nextEnv = setmetatable({}, { __index = env })
+ if cond then
+ processBlock(s[2], nextEnv)
+ elseif s[3] then
+ processBlock(s[3], nextEnv)
+ end
+ end
+ end
+ end
+ local env = setmetatable({}, { __index = _G })
+ processBlock(ast, env)
+ end
+ if traceOut then
+ traceOut:close()
+ end
+ end
+end
+---------------------------------------------------------------------------------------------------------------------------
+do
+ for _, thisModuleName in ipairs(depOrderedModuleNames) do
+
+ local thisModule = modules[thisModuleName]
+ local thisModuleInfo = moduleInfos[thisModuleName]
+
+ ---------------------------------------------------------------
+ parseModule(thisModuleName, thisModule, thisModuleInfo)
+ ---------------------------------------------------------------
+
+ -- isMethod is only known after parsing the function declaration
+ --
+ if not thisModuleInfo.isInternal and thisModuleInfo.isClass --[[or thisModuleInfo.isMixin]] then
+ local methods = {}
+ local functions = {}
+ if not thisModuleInfo.memberFunctions then
+ error(require"inspect"{thisModuleName})
+ end
+ for _, f in ipairs(thisModuleInfo.memberFunctions) do
+ local isMethod = f.isMethod
+ if isMethod == nil then
+ if not f.originalMemberInfo then
+ error(require"inspect"{thisModuleName, f})
+ end
+ isMethod = f.originalMemberInfo.isMethod
+ assert(type(isMethod) == "boolean", f.memberName)
+ elseif f.originalMemberInfo then
+ assert(isMethod == f.originalMemberInfo.isMethod)
+ end
+ if isMethod then
+ add(methods, f.memberName, f)
+ else
+ add(functions, f.memberName, f)
+ end
+ end
+ thisModuleInfo.methods = methods
+ thisModuleInfo.functions = functions
+ end
+ end
+end
+---------------------------------------------------------------------------------------------------------------------------
+do
+ for _, info in ipairs(classMixinInfos) do
+ local subClassPaths = info.subClassPaths
+ table.sort(subClassPaths)
+ local subClassTree = {}
+ for _, p in ipairs(subClassPaths) do
+ local r = subClassTree
+ for n in p:gmatch("[^/]+") do
+ local r2 = r[n]
+ if not r2 then
+ r2 = { moduleName = n }
+ add(r, n, r2)
+ end
+ r = r2
+ end
+ end
+ info.subClassTree = subClassTree
+ end
+end
+---------------------------------------------------------------------------------------------------------------------------
+do
+ local function compareMixinRealisation(a, b)
+ return a.pathString < b.pathString
+ end
+ for _, mixinInfo in ipairs(mixinInfos) do
+ local reals = mixinInfo.mixinRealisations
+ table.sort(reals, compareMixinRealisation)
+ local mixinSupers = {}
+ for _, r in ipairs(reals) do
+ local s = mixinSupers
+ for _, e in ipairs(r) do
+ local s2 = s[e.moduleName]
+ if not s2 then
+ s2 = { moduleName = e.moduleName, isMixin = e.isMixin }
+ add(s, e.moduleName, s2)
+ end
+ s = s2
+ end
+ end
+ mixinInfo.mixinSupers = mixinSupers
+ end
+end
+
+perf.stop"totalparse"
+
+---------------------------------------------------------------------------------------------------------------------------
+
+local function sortMembers(members)
+ if members then
+ table.sort(members, function(a,b)
+ local an, bn = a.memberName, b.memberName
+ local am, bm = an:match("^_"), bn:match("^_")
+ if (am and bm) or (not am and not bm) then
+ return an < bn
+ else
+ return not am
+ end
+ end)
+ end
+end
+
+local function sortInheritedMembers(superClassList)
+ if superClassList then
+ for i = #superClassList, 1, -1 do
+ local methods = superClassList[i]
+ if #methods > 0 then
+ sortMembers(methods)
+ else
+ table.remove(superClassList, i)
+ end
+ end
+ end
+end
+
+local function sortModules(modules, deep)
+ table.sort(modules, function(a,b) return a.moduleName < b.moduleName end)
+ if deep then
+ for _, module in ipairs(modules) do
+ if module.methods then
+ sortMembers(module.methods)
+ end
+ if module.functions then
+ sortMembers(module.functions)
+ end
+ if module.inheritedMethods then
+ sortInheritedMembers(module.inheritedMethods)
+ end
+ end
+ end
+end
+
+sortModules(moduleInfos, true)
+sortModules(packageInfos)
+
+local function newSubstLinks(genDirPrefix)
+
+ local linkFormat = "[%s]("..genDirPrefix.."%s.md)"
+ local linkPattern = ( C( (S'["' * P"lwtk.")
+ + (-(S'["' * P"lwtk.") * -P"lwtk." * P(1))^1
+ )
+ + ((P"lwtk." * (alnum + P".")^1) /
+ function(m)
+ if lwtk.tryrequire(m) then
+ return format(linkFormat, m, m:gsub("%.", "/"))
+ else
+ return m
+ end
+ end
+ )
+ )^0 / function(...) return table.concat { ... } end
+
+ return function(docString)
+ return lpeg.match(linkPattern, docString)
+ end
+end
+
+---------------------------------------------------------------------------------------------------------------------------
+perf.start"output"
+
+do
+ local substLinks = newSubstLinks("")
+
+ local sections = {
+ { "Classes", classInfos },
+ { "Mixins", mixinInfos },
+ { "Metas", metaInfos },
+ { "Functions", functionInfos },
+ { "Other", otherInfos }
+ }
+ for _, s in ipairs(sections) do
+ sortModules(s[2])
+ for _, info in ipairs(s[2]) do
+ if not info.isPackage then
+ local packInfo = moduleInfos[info.packageName]
+ local infos = packInfo[s[2]]
+ if not infos then
+ infos = {}
+ packInfo[s[2]] = infos
+ end
+ add(infos, info.moduleName, info)
+ end
+ end
+ end
+
+ local outName = "../doc/gen/modules.md"
+ local out = io.open(outName, "w")
+ do
+ local function pr(...)
+ fprintf(out, ...)
+ end
+ pr("# Module Index\n\n")
+ for _, p in ipairs(packageInfos) do
+ if not p.isInternal then
+ pr(" * [%s](#%s)", p.moduleName, p.moduleName:gsub("%.",""))
+ if p.shortDoc then
+ pr(" - %s", substLinks(p.shortDoc))
+ end
+ pr("\n")
+ end
+ end
+ for _, p in ipairs(packageInfos) do
+ local packageName = p.moduleName
+ if not p.isInternal then
+ pr("\n## %s\n", packageName)
+ pr("\n")
+ for _, s in ipairs(sections) do
+ local infos = p[s[2]]
+ if infos then
+ pr("### %s\n", s[1])
+ for _, m in ipairs(infos) do
+ if m.packageName == packageName then
+ local b = m.isClass and "**" or ""
+ pr(" * %s[%s](%s)%s", b, m.moduleName, m.docFileName, b)
+ if m.shortDoc then
+ pr(" - %s", substLinks(m.shortDoc))
+ end
+ pr("\n")
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ out:close()
+end
+---------------------------------------------------------------------------------------------------------------------------
+do
+ for _, m in ipairs(moduleInfos) do
+ if not m.isInternal then
+ local genDirPrefix = string.rep("../", #m.moduleName:gsub("[^.]*(%.?)[^.]*", "%1"))
+ local substLinks = newSubstLinks(genDirPrefix)
+ local shortModuleName = m.moduleName:gsub("^lwtk%.", "")
+ local out = assert(io.open(path("../doc/gen", m.docFileName), "w"))
+ do
+ local function pr(...)
+ fprintf(out, ...)
+ end
+ local function prind(n)
+ for i = 1, n do
+ out:write(" ")
+ end
+ end
+ local module = modules[m.moduleName]
+ local tp
+ if type(module) == "function" then
+ tp = "Function "
+ elseif m.isClass then
+ tp = "Class "
+ elseif m.isMixin then
+ tp = "Mixin "
+ elseif m.isMeta then
+ tp = "Meta "
+ elseif type(module) == "table" then
+ tp = "Table "
+ elseif type(module) == "number" or type(module) == "string" then
+ tp = ""
+ else
+ error(m.moduleName)
+ end
+ pr("# %s%s\n\n", tp, m.moduleName)
+ if m.moduleType == "function" then
+ local argListString = m.argListString
+ if m.isMethod and argListString then
+ argListString = "self, "..argListString
+ end
+ pr(" * **`%s(%s)`**\n\n", shortModuleName,
+ argListString or "?")
+ if m.fullDoc then
+ pr(util.indent(5, substLinks(m.fullDoc)))
+ end
+ else
+ if m.fullDoc then
+ pr("%s\n", substLinks(m.fullDoc))
+ end
+ end
+ ------------------------------------------------------------------------
+ local contentsTitlePrinted = false
+ local function assureContentsTitle()
+ if not contentsTitlePrinted then
+ pr("\n")
+ pr("## Contents\n\n")
+ contentsTitlePrinted = true
+ end
+ end
+ local classPathList
+ if m.isClass then
+ classPathList = util.getClassPathList(module)
+ if #classPathList > 1 then
+ assureContentsTitle()
+ pr(" * [Inheritance](#inheritance)\n")
+ end
+ elseif m.isMixin then
+ if #m.mixinRealisations > 0 then
+ assureContentsTitle()
+ pr(" * [Inheritance](#inheritance)\n")
+ end
+ end
+ local hasNewOk, hasNew = pcall(function() return module["new"] end)
+ hasNew = hasNewOk and hasNew
+ local hasConstructor = (m.isMeta or m.isClass) and hasNew
+ if hasConstructor then
+ assureContentsTitle()
+ pr(" * [Constructor](#constructor)\n")
+ end
+ local methodFunctionSections = {
+ { "Methods", "methods", m.methods },
+ { "Functions", "functions", m.functions }
+ }
+ for _, section in pairs(methodFunctionSections) do
+ if section[3] and #section[3] > 0 then
+ assureContentsTitle()
+ pr(" * [%s](#%s)\n", section[1], section[2])
+ for _, meth in ipairs(section[3]) do
+ pr(" * [%s()](#.%s)", meth.memberName, meth.memberName)
+ if meth.shortDoc then
+ pr(" - %s", meth.shortDoc)
+ else
+ local orig = meth.originalMemberInfo
+ if orig and orig.shortDoc then
+ pr(" - %s", orig.shortDoc)
+ end
+ end
+ pr("\n")
+ end
+ end
+ end
+ if m.inheritedMethods and #m.inheritedMethods > 0 then
+ assureContentsTitle()
+ pr(" * [Inherited Methods](#inherited-methods)\n")
+ end
+ if (m.isClass or m.isMixin) and #m.subClassPaths > 0 then
+ assureContentsTitle()
+ pr(" * [Subclasses](#subclasses)\n")
+ end
+ ------------------------------------------------------------------------
+ pr("\n")
+ if m.isClass and #classPathList > 1 then
+ pr("\n## Inheritance\n")
+ --pr(" * Super class path:\n", #classPathList>2 and "es" or "")
+ pr(" * ")
+ for i = #classPathList, 1, -1 do
+ pr(" / ")
+ local c = classPathList[i]
+ local cname = c.moduleName
+ local b = c.isMixin and "" or "**"
+ if cname == m.moduleName then
+ pr("_%s`%s`%s_", b,
+ cname:gsub("^lwtk%.",""),
+ b)
+ else
+ pr("%s[%s](%s%s.md#inheritance)%s",
+ b,
+ cname:gsub("^lwtk%.",""),
+ genDirPrefix,
+ cname:gsub("%.","/"),
+ b)
+ end
+ end
+ pr("\n")
+ elseif m.isMixin and #m.mixinRealisations > 0 then
+ pr("\n## Inheritance\n")
+ local function prSup(indn, sup, top)
+ for _, s0 in ipairs(sup) do
+ local s = s0
+ prind(indn) pr(" * ")
+ if top then
+ pr("/ ")
+ end
+ while true do
+ if s ~= s0 then
+ pr(" / ")
+ end
+ local sname = s.moduleName
+ local b = s.isMixin and "" or "**"
+ if sname == m.moduleName then
+ pr("_%s`%s`%s_", b, sname:gsub("^lwtk%.",""),
+ b)
+ else
+ pr("%s[%s](%s%s.md#inheritance)%s", b, sname:gsub("^lwtk%.",""),
+ genDirPrefix,
+ sname:gsub("%.","/"),
+ b)
+ end
+ if #s ~= 1 then
+ break
+ end
+ s = s[1]
+ end
+ if #s > 1 then
+ pr(" /\n")
+ prSup(indn + 5, s)
+ else
+ pr("\n")
+ end
+ end
+ end
+ prSup(0, m.mixinSupers, true)
+ end
+ if hasConstructor then
+ pr("\n## Constructor\n")
+ if m.isMeta or module.declared["new"] then
+ local newInfo = assert(m.memberInfos["new"])
+ local argListString = newInfo.argListString
+ pr(" * **`%s(%s)`**\n\n", ".new", shortModuleName,
+ argListString or "?")
+ if newInfo.fullDoc then
+ pr(util.indent(5, newInfo.fullDoc))
+ pr("\n")
+ end
+ local overrideList = m.isClass and util.getOverrideList(module, "new")
+ if overrideList then
+ local indent = " "
+ --pr("%s * Overrides:\n", indent)
+ for i, ov in ipairs(overrideList) do
+ local b = ov.isMixin and "" or "**"
+ pr("%s * %s: %s[%s()](%s%s.md#constructor)%s\n", string.rep(indent, i),
+ ov.overrideText,
+ b,
+ ov.moduleName:gsub("^lwtk%.", ""),
+ genDirPrefix,
+ ov.moduleName:gsub("%.", "/"),
+ b)
+ end
+ pr("\n")
+ end
+ else
+ local inheritedFrom = util.findDeclaratingSuperClass(module, "new")
+ local inheritedFromInfo = assert(moduleInfos[inheritedFrom])
+ local newInfo = assert(inheritedFromInfo.memberInfos["new"])
+ local argListString = newInfo.argListString
+ pr(" * **`%s(%s)`**\n\n", ".new", shortModuleName,
+ argListString or "?")
+ local b = inheritedFromInfo.isClass and "**" or ""
+ pr(" * Inherited from: %s[%s()](%s%s.md#constructor)%s\n", b, inheritedFrom.__name:gsub("^lwtk%.",""),
+ genDirPrefix,
+ inheritedFrom.__name:gsub("%.", "/"),
+ b)
+ end
+ pr("\n")
+ end
+ for _, section in pairs(methodFunctionSections) do
+ if section[3] and #section[3] > 0 then
+ pr("\n## %s\n", section[1])
+ for _, meth in ipairs(section[3]) do
+ --print("#####################", require"inspect"{meth.methodName, moduleDoc.membersDoc})
+ local argListString = meth.argListString
+ local isMethod = meth.isMethod
+ local original = meth.originalMemberInfo
+ if not argListString and original then
+ argListString = original.argListString
+ isMethod = original.isMethod
+ end
+ local sep = isMethod and ":" or "."
+ pr(" * **`%s%s%s(%s)`**\n\n", "."..meth.memberName, shortModuleName, sep, meth.memberName,
+ argListString or "?")
+ if meth.fullDoc then
+ pr(util.indent(5, meth.fullDoc))
+ pr("\n")
+ elseif original and original.fullDoc then
+ pr(util.indent(5, original.fullDoc))
+ pr("\n")
+ end
+ if original then
+ if original.isModule then
+ pr(" * Implementation: [%s()](%s%s.md)\n",
+ original.moduleName,
+ genDirPrefix,
+ original.moduleName:gsub("%.","/"))
+ else
+ pr(" * Implementation: [%s:%s()](%s%s.md#%s)\n",
+ original.moduleName:gsub("^lwtk%.",""),
+ original.memberName,
+ genDirPrefix,
+ original.moduleName:gsub("%.","/"),
+ original.memberName)
+ end
+ end
+ if meth.overrideList then
+ local indent = " "
+ for i, ov in ipairs(meth.overrideList) do
+ pr("%s * %s: [%s:%s()](%s%s.md#.%s)\n", string.rep(indent, i),
+ ov.overrideText,
+ ov.moduleName:gsub("^lwtk%.", ""),
+ meth.memberName,
+ genDirPrefix,
+ ov.moduleName:gsub("%.", "/"),
+ meth.memberName)
+ end
+ pr("\n")
+ end
+ pr("\n")
+ end
+ end
+ end
+ if m.inheritedMethods and #m.inheritedMethods > 0 then
+ pr("\n## Inherited Methods\n")
+ for _, fromList in ipairs(m.inheritedMethods) do
+ local b = fromList.isMixin and "" or "**"
+ local fromFile = format("%s%s.md", genDirPrefix, fromList.moduleName:gsub("%.", "/"))
+ pr(" * %s[%s](%s)%s:\n", b, fromList.moduleName:gsub("^lwtk%.",""), fromFile, b)
+ pr(" * ")
+ for i, fr in ipairs(fromList) do
+ if i > 1 then pr(", ") end
+ pr("[%s()](%s#.%s)", fr.memberName, fromFile, fr.memberName)
+ end
+ pr("\n")
+ end
+ end
+ if (m.isClass or m.isMixin) and #m.subClassPaths > 0 then
+ pr("\n## Subclasses\n")
+ --pr(" * Sub class path%s:\n", #m.subClassTree[1] > 1 and "s" or "")
+ local function prSub(indn, sub, top)
+ for _, s0 in ipairs(sub) do
+ local s = s0
+ prind(indn) pr(" * ")
+ if top then
+ pr("/ ")
+ end
+ while true do
+ if s ~= s0 then
+ pr(" / ")
+ end
+ local sname = s.moduleName
+ local sinfo = moduleInfos[sname]
+ local b = sinfo.isMixin and "" or "**"
+ local hasSubclass = sinfo.subClassPaths and #sinfo.subClassPaths > 0
+ if m.moduleName == sname then
+ pr("_%s`%s`%s_", b, sname:gsub("^lwtk%.",""),
+ b)
+ else
+ pr("%s[%s](%s%s.md#%s)%s", b, sname:gsub("^lwtk%.",""),
+ genDirPrefix,
+ sname:gsub("%.","/"),
+ hasSubclass and "subclasses" or "inheritance",
+ b)
+ end
+ if #s ~= 1 then
+ break
+ end
+ s = s[1]
+ end
+ if #s > 1 then
+ pr(" /\n")
+ prSub(indn + 5, s)
+ else
+ pr("\n")
+ end
+ end
+ end
+ prSub(0, m.subClassTree, true)
+ --pr("%s", pprint.pformat(m.subClassTree))
+ pr("\n")
+ end
+ end
+ out:close()
+ end
+ end
+end
+perf.stop"output"
+---------------------------------------------------------------------------------------------------------------------------
+
+perf.finish()
diff --git a/src/mkdoc/pprint.lua b/src/mkdoc/pprint.lua
new file mode 100644
index 0000000..d08b5e6
--- /dev/null
+++ b/src/mkdoc/pprint.lua
@@ -0,0 +1,518 @@
+-- https://github.com/jagt/pprint.lua
+--[[
+Chen Tao - jagttt@gmail.com
+
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+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 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.
+
+For more information, please refer to
+--]]
+local pprint = { VERSION = '0.1' }
+
+local depth = 1
+
+pprint.defaults = {
+ -- If set to number N, then limit table recursion to N deep.
+ depth_limit = false,
+ -- type display trigger, hide not useful datatypes by default
+ -- custom types are treated as table
+ show_nil = true,
+ show_boolean = true,
+ show_number = true,
+ show_string = true,
+ show_table = true,
+ show_function = false,
+ show_thread = false,
+ show_userdata = false,
+ -- additional display trigger
+ show_metatable = false, -- show metatable
+ show_all = false, -- override other show settings and show everything
+ use_tostring = false, -- use __tostring to print table if available
+ filter_function = nil, -- called like callback(value[,key, parent]), return truty value to hide
+ object_cache = 'local', -- cache blob and table to give it a id, 'local' cache per print, 'global' cache
+ -- per process, falsy value to disable (might cause infinite loop)
+ -- format settings
+ indent_size = 2, -- indent for each nested table level
+ level_width = 80, -- max width per indent level
+ wrap_string = true, -- wrap string when it's longer than level_width
+ wrap_array = true, -- wrap every array elements
+ sort_keys = true, -- sort table keys
+}
+
+local TYPES = {
+ ['nil'] = 1, ['boolean'] = 2, ['number'] = 3, ['string'] = 4,
+ ['table'] = 5, ['function'] = 6, ['thread'] = 7, ['userdata'] = 8
+}
+
+-- seems this is the only way to escape these, as lua don't know how to map char '\a' to 'a'
+local ESCAPE_MAP = {
+ ['\a'] = '\\a', ['\b'] = '\\b', ['\f'] = '\\f', ['\n'] = '\\n', ['\r'] = '\\r',
+ ['\t'] = '\\t', ['\v'] = '\\v', ['\\'] = '\\\\',
+}
+
+-- generic utilities
+local function escape(s)
+ s = s:gsub('([%c\\])', ESCAPE_MAP)
+ local dq = s:find('"')
+ local sq = s:find("'")
+ if dq and sq then
+ return s:gsub('"', '\\"'), '"'
+ elseif sq then
+ return s, '"'
+ else
+ return s, "'"
+ end
+end
+
+local function is_plain_key(key)
+ return type(key) == 'string' and key:match('^[%a_][%a%d_]*$')
+end
+
+local CACHE_TYPES = {
+ ['table'] = true, ['function'] = true, ['thread'] = true, ['userdata'] = true
+}
+
+-- cache would be populated to be like:
+-- {
+-- function = { `fun1` = 1, _cnt = 1 }, -- object id
+-- table = { `table1` = 1, `table2` = 2, _cnt = 2 },
+-- visited_tables = { `table1` = 7, `table2` = 8 }, -- visit count
+-- }
+-- use weakrefs to avoid accidentall adding refcount
+local function cache_apperance(obj, cache, option)
+ if not cache.visited_tables then
+ cache.visited_tables = setmetatable({}, {__mode = 'k'})
+ end
+ local t = type(obj)
+
+ -- TODO can't test filter_function here as we don't have the ix and key,
+ -- might cause different results?
+ -- respect show_xxx and filter_function to be consistent with print results
+ if (not TYPES[t] and not option.show_table)
+ or (TYPES[t] and not option['show_'..t]) then
+ return
+ end
+
+ if CACHE_TYPES[t] or TYPES[t] == nil then
+ if not cache[t] then
+ cache[t] = setmetatable({}, {__mode = 'k'})
+ cache[t]._cnt = 0
+ end
+ if not cache[t][obj] then
+ cache[t]._cnt = cache[t]._cnt + 1
+ cache[t][obj] = cache[t]._cnt
+ end
+ end
+ if t == 'table' or TYPES[t] == nil then
+ if cache.visited_tables[obj] == false then
+ -- already printed, no need to mark this and its children anymore
+ return
+ elseif cache.visited_tables[obj] == nil then
+ cache.visited_tables[obj] = 1
+ else
+ -- visited already, increment and continue
+ cache.visited_tables[obj] = cache.visited_tables[obj] + 1
+ return
+ end
+ for k, v in pairs(obj) do
+ cache_apperance(k, cache, option)
+ cache_apperance(v, cache, option)
+ end
+ local mt = getmetatable(obj)
+ if mt and option.show_metatable then
+ cache_apperance(mt, cache, option)
+ end
+ end
+end
+
+-- makes 'foo2' < 'foo100000'. string.sub makes substring anyway, no need to use index based method
+local function str_natural_cmp(lhs, rhs)
+ while #lhs > 0 and #rhs > 0 do
+ local lmid, lend = lhs:find('%d+')
+ local rmid, rend = rhs:find('%d+')
+ if not (lmid and rmid) then return lhs < rhs end
+
+ local lsub = lhs:sub(1, lmid-1)
+ local rsub = rhs:sub(1, rmid-1)
+ if lsub ~= rsub then
+ return lsub < rsub
+ end
+
+ local lnum = tonumber(lhs:sub(lmid, lend))
+ local rnum = tonumber(rhs:sub(rmid, rend))
+ if lnum ~= rnum then
+ return lnum < rnum
+ end
+
+ lhs = lhs:sub(lend+1)
+ rhs = rhs:sub(rend+1)
+ end
+ return lhs < rhs
+end
+
+local function cmp(lhs, rhs)
+ local tleft = type(lhs)
+ local tright = type(rhs)
+ if tleft == 'number' and tright == 'number' then return lhs < rhs end
+ if tleft == 'string' and tright == 'string' then return str_natural_cmp(lhs, rhs) end
+ if tleft == tright then return str_natural_cmp(tostring(lhs), tostring(rhs)) end
+
+ -- allow custom types
+ local oleft = TYPES[tleft] or 9
+ local oright = TYPES[tright] or 9
+ return oleft < oright
+end
+
+-- setup option with default
+local function make_option(option)
+ if option == nil then
+ option = {}
+ end
+ for k, v in pairs(pprint.defaults) do
+ if option[k] == nil then
+ option[k] = v
+ end
+ if option.show_all then
+ for t, _ in pairs(TYPES) do
+ option['show_'..t] = true
+ end
+ option.show_metatable = true
+ end
+ end
+ return option
+end
+
+-- override defaults and take effects for all following calls
+function pprint.setup(option)
+ pprint.defaults = make_option(option)
+end
+
+-- format lua object into a string
+function pprint.pformat(obj, option, printer)
+ option = make_option(option)
+ local buf = {}
+ local function default_printer(s)
+ table.insert(buf, s)
+ end
+ printer = printer or default_printer
+
+ local cache
+ if option.object_cache == 'global' then
+ -- steal the cache into a local var so it's not visible from _G or anywhere
+ -- still can't avoid user explicitly referentce pprint._cache but it shouldn't happen anyway
+ cache = pprint._cache or {}
+ pprint._cache = nil
+ elseif option.object_cache == 'local' then
+ cache = {}
+ end
+
+ local last = '' -- used for look back and remove trailing comma
+ local status = {
+ indent = '', -- current indent
+ len = 0, -- current line length
+ }
+
+ local wrapped_printer = function(s)
+ printer(last)
+ last = s
+ end
+
+ local function _indent(d)
+ status.indent = string.rep(' ', d + #(status.indent))
+ end
+
+ local function _n(d)
+ wrapped_printer('\n')
+ wrapped_printer(status.indent)
+ if d then
+ _indent(d)
+ end
+ status.len = 0
+ return true -- used to close bracket correctly
+ end
+
+ local function _p(s, nowrap)
+ status.len = status.len + #s
+ if not nowrap and status.len > option.level_width then
+ _n()
+ wrapped_printer(s)
+ status.len = #s
+ else
+ wrapped_printer(s)
+ end
+ end
+
+ local formatter = {}
+ local function format(v)
+ local f = formatter[type(v)]
+ f = f or formatter.table -- allow patched type()
+ if option.filter_function and option.filter_function(v, nil, nil) then
+ return ''
+ else
+ return f(v)
+ end
+ end
+
+ local function tostring_formatter(v)
+ return tostring(v)
+ end
+
+ local function number_formatter(n)
+ return n == math.huge and '[[math.huge]]' or tostring(n)
+ end
+
+ local function nop_formatter(v)
+ return ''
+ end
+
+ local function make_fixed_formatter(t, has_cache)
+ if has_cache then
+ return function (v)
+ return string.format('[[%s %d]]', t, cache[t][v])
+ end
+ else
+ return function (v)
+ return '[['..t..']]'
+ end
+ end
+ end
+
+ local function string_formatter(s, force_long_quote)
+ local s, quote = escape(s)
+ local quote_len = force_long_quote and 4 or 2
+ if quote_len + #s + status.len > option.level_width then
+ _n()
+ -- only wrap string when is longer than level_width
+ if option.wrap_string and #s + quote_len > option.level_width then
+ -- keep the quotes together
+ _p('[[')
+ while #s + status.len >= option.level_width do
+ local seg = option.level_width - status.len
+ _p(string.sub(s, 1, seg), true)
+ _n()
+ s = string.sub(s, seg+1)
+ end
+ _p(s) -- print the remaining parts
+ return ']]'
+ end
+ end
+
+ return force_long_quote and '[['..s..']]' or quote..s..quote
+ end
+
+ local function table_formatter(t)
+ if option.use_tostring then
+ local mt = getmetatable(t)
+ if mt and mt.__tostring then
+ return string_formatter(tostring(t), true)
+ end
+ end
+
+ local print_header_ix = nil
+ local ttype = type(t)
+ if option.object_cache then
+ local cache_state = cache.visited_tables[t]
+ local tix = cache[ttype][t]
+ -- FIXME should really handle `cache_state == nil`
+ -- as user might add things through filter_function
+ if cache_state == false then
+ -- already printed, just print the the number
+ return string_formatter(string.format('%s %d', ttype, tix), true)
+ elseif cache_state > 1 then
+ -- appeared more than once, print table header with number
+ print_header_ix = tix
+ cache.visited_tables[t] = false
+ else
+ -- appeared exactly once, print like a normal table
+ end
+ end
+
+ local limit = tonumber(option.depth_limit)
+ if limit and depth > limit then
+ if print_header_ix then
+ return string.format('[[%s %d]]...', ttype, print_header_ix)
+ end
+ return string_formatter(tostring(t), true)
+ end
+
+ local tlen = #t
+ local wrapped = false
+ _p('{')
+ _indent(option.indent_size)
+ _p(string.rep(' ', option.indent_size - 1))
+ if print_header_ix then
+ _p(string.format('--[[%s %d]] ', ttype, print_header_ix))
+ end
+ local wrap_array = option.wrap_array
+ if wrap_array then
+ local allvalues = true
+ for ix = 1,tlen do
+ local tp = type(t[ix])
+ if tp ~= "string" and tp ~= "number" and tp ~= "boolean" then
+ allvalues = false
+ break
+ end
+ end
+ if allvalues then
+ wrap_array = false
+ end
+ end
+ for ix = 1,tlen do
+ local v = t[ix]
+ if formatter[type(v)] == nop_formatter or
+ (option.filter_function and option.filter_function(v, ix, t)) then
+ -- pass
+ else
+ if wrap_array then
+ wrapped = _n()
+ end
+ depth = depth+1
+ _p(format(v)..', ')
+ depth = depth-1
+ end
+ end
+
+ -- hashmap part of the table, in contrast to array part
+ local function is_hash_key(k)
+ if type(k) ~= 'number' then
+ return true
+ end
+
+ local numkey = math.floor(tonumber(k))
+ if numkey ~= k or numkey > tlen or numkey <= 0 then
+ return true
+ end
+ end
+
+ local function print_kv(k, v, t)
+ -- can't use option.show_x as obj may contain custom type
+ if formatter[type(v)] == nop_formatter or
+ formatter[type(k)] == nop_formatter or
+ (option.filter_function and option.filter_function(v, k, t)) then
+ return
+ end
+ wrapped = _n()
+ if is_plain_key(k) then
+ _p(k, true)
+ else
+ _p('[')
+ -- [[]] type string in key is illegal, needs to add spaces inbetween
+ local k = format(k)
+ if string.match(k, '%[%[') then
+ _p(' '..k..' ', true)
+ else
+ _p(k, true)
+ end
+ _p(']')
+ end
+ _p(' = ', true)
+ depth = depth+1
+ _p(format(v), true)
+ depth = depth-1
+ _p(',', true)
+ end
+
+ if option.sort_keys then
+ local keys = {}
+ for k, _ in pairs(t) do
+ if is_hash_key(k) then
+ table.insert(keys, k)
+ end
+ end
+ table.sort(keys, cmp)
+ for _, k in ipairs(keys) do
+ print_kv(k, t[k], t)
+ end
+ else
+ for k, v in pairs(t) do
+ if is_hash_key(k) then
+ print_kv(k, v, t)
+ end
+ end
+ end
+
+ if option.show_metatable then
+ local mt = getmetatable(t)
+ if mt then
+ print_kv('__metatable', mt, t)
+ end
+ end
+
+ _indent(-option.indent_size)
+ -- make { } into {}
+ last = string.gsub(last, '^ +$', '')
+ -- peek last to remove trailing comma
+ last = string.gsub(last, ',%s*$', ' ')
+ if wrapped then
+ _n()
+ end
+ _p('}')
+
+ return ''
+ end
+
+ -- set formatters
+ formatter['nil'] = option.show_nil and tostring_formatter or nop_formatter
+ formatter['boolean'] = option.show_boolean and tostring_formatter or nop_formatter
+ formatter['number'] = option.show_number and number_formatter or nop_formatter -- need to handle math.huge
+ formatter['function'] = option.show_function and make_fixed_formatter('function', option.object_cache) or nop_formatter
+ formatter['thread'] = option.show_thread and make_fixed_formatter('thread', option.object_cache) or nop_formatter
+ formatter['userdata'] = option.show_userdata and make_fixed_formatter('userdata', option.object_cache) or nop_formatter
+ formatter['string'] = option.show_string and string_formatter or nop_formatter
+ formatter['table'] = option.show_table and table_formatter or nop_formatter
+
+ if option.object_cache then
+ -- needs to visit the table before start printing
+ cache_apperance(obj, cache, option)
+ end
+
+ _p(format(obj))
+ printer(last) -- close the buffered one
+
+ -- put cache back if global
+ if option.object_cache == 'global' then
+ pprint._cache = cache
+ end
+
+ return table.concat(buf)
+end
+
+-- pprint all the arguments
+function pprint.pprint( ... )
+ local args = {...}
+ -- select will get an accurate count of array len, counting trailing nils
+ local len = select('#', ...)
+ for ix = 1,len do
+ pprint.pformat(args[ix], nil, io.write)
+ io.write('\n')
+ end
+end
+
+setmetatable(pprint, {
+ __call = function (_, ...)
+ pprint.pprint(...)
+ end
+})
+
+return pprint
+
diff --git a/src/mkdoc/util.lua b/src/mkdoc/util.lua
new file mode 100644
index 0000000..06f6998
--- /dev/null
+++ b/src/mkdoc/util.lua
@@ -0,0 +1,141 @@
+local lwtk = require"lwtk"
+
+local insert = table.insert
+local getMixinBase = lwtk.get.mixinBase
+
+local util = {}
+
+function util.findDeclaratingSuperClass(thisClass, memberName)
+ local s = thisClass:getSuperClass()
+ while s do
+ if s.declared[memberName] then
+ return s
+ end
+ s = s:getSuperClass()
+ end
+end
+
+
+function util.getClassModuleName(class)
+ local name = class.__name
+ local mixinBase = getMixinBase[class]
+ if mixinBase then
+ return mixinBase.__name
+ else
+ return name
+ end
+
+end
+local getModuleName = util.getClassModuleName
+
+function util.getOverrideList(thisClass, memberName)
+ local rslt = {}
+ local s = thisClass:getSuperClass()
+ while s do
+ if s.declared[memberName] then
+ local overrideText = (s[memberName] == false) and "Implements" or "Overrides"
+ local entry = { moduleName = getModuleName(s), overrideText = overrideText, isMixin = getMixinBase[s] and true }
+ rslt[s.__name] = entry
+ rslt[#rslt + 1] = entry
+ end
+ s = s:getSuperClass()
+ end
+ if #rslt > 0 then
+ return rslt
+ end
+end
+
+function util.getSuperClassList(thisClass, endClass)
+ local rslt = {}
+ local s = thisClass:getSuperClass()
+ while s do
+ local entry = { moduleName = getModuleName(s), isMixin = getMixinBase[s] and true }
+ rslt[#rslt + 1] = entry
+ if endClass and s == endClass then
+ break
+ end
+ s = s:getSuperClass()
+ end
+ return rslt
+end
+
+function util.getNonMixinSuperClassWithList(thisClass)
+ local list = {}
+ local s = thisClass:getSuperClass()
+ while s do
+ list[#list + 1] = s.__name
+ if getMixinBase[s] == nil then
+ return s, list
+ end
+ s = s:getSuperClass()
+ end
+end
+
+function util.getClassPathList(thisClass, endClass)
+ local rslt = {}
+ local s = thisClass
+ while s do
+ local entry = { moduleName = getModuleName(s), isMixin = getMixinBase[s] and true }
+ rslt[s.__name] = entry
+ rslt[#rslt + 1] = entry
+ if endClass and s == endClass then
+ break
+ end
+ s = s:getSuperClass()
+ end
+ return rslt
+end
+
+function util.getReverseClassPathList(thisClass, endClass)
+ local list = util.getClassPathList(thisClass, endClass)
+ local rslt = {}
+ local n = #list
+ for i, e in ipairs(list) do
+ rslt[n-i+1] = e
+ end
+ return rslt
+end
+
+function util.getReverseClassPathString(thisClass, endClass)
+ local list = util.getClassPathList(thisClass, endClass)
+ local rslt = {}
+ local n = #list
+ for i, e in ipairs(list) do
+ rslt[n-i+1] = e.moduleName
+ end
+ return table.concat(rslt, "/")
+end
+
+function util.classPathListToString(list)
+ local rslt = {}
+ for i, e in ipairs(list) do
+ rslt[i] = e.moduleName
+ end
+ return table.concat(rslt, "/")
+end
+
+
+function util.indent(indent, comment)
+ local indentSting = string.rep(" ", indent)
+ return comment:gsub("^", indentSting):gsub("\n", "\n"..indentSting)
+end
+
+function util.getArgListString(exp)
+ if exp and exp.tag == "Function" then
+ local argl = exp[1]
+ local isMethod = (#argl >= 1 and argl[1][1] == "self")
+ local rslt = {}
+ for i = isMethod and 2 or 1, #argl do
+ if argl[i].tag == "Dots" then
+ rslt[#rslt + 1] = "..."
+ else
+ assert(argl[i].tag == "Id", require"inspect"{argl[i]})
+ rslt[#rslt + 1] = argl[i][1]
+ end
+ end
+ return table.concat(rslt, ", "), isMethod
+ end
+end
+
+
+return util
diff --git a/src/perf.lua b/src/perf.lua
new file mode 100644
index 0000000..7377915
--- /dev/null
+++ b/src/perf.lua
@@ -0,0 +1,79 @@
+local time = require"chronos".nanotime -- https://luarocks.org/modules/ldrumm/chronos
+
+local perf = {}
+
+local t0
+local timers = {}
+
+function perf.reset()
+ t0 = nil
+ timers = {}
+end
+
+function perf.start(name)
+ if not t0 then
+ t0 = time()
+ end
+ local timer = timers[name]
+ if not timer then
+ local timer = { name = name, t0 = time(), dt = 0, count = 1, started = 1 }
+ timers[name] = timer
+ timers[#timers + 1] = timer
+ else
+ local started = timer.started
+ if started > 0 then
+ timer.started = started + 1
+ else
+ timer.t0 = time()
+ timer.started = 1
+ end
+ timer.count = timer.count + 1
+ end
+end
+
+function perf.stop(name)
+ local timer = timers[name]
+ local started = timer.started
+ started = started - 1
+ if started == 0 then
+ local dt = time() - timer.t0
+ timer.dt = timer.dt + dt
+ end
+ timer.started = started
+end
+
+function perf.finish()
+ print("----------------------------------------------------------------------------")
+ local t1 = time()
+ local nlen = 0
+ local tlen = 0
+ local clen = 0
+ do
+ for _, timer in ipairs(timers) do
+ local len = #timer.name
+ if len > nlen then
+ nlen = len
+ end
+ local len = #string.format("%d", math.ceil(timer.dt * 1000))
+ local len = #string.format("%d", timer.count)
+ if len > clen then
+ clen = len
+ end
+ end
+ nlen = nlen + 8
+ tlen = 4 + #string.format("%d", math.ceil((t1 - t0) * 1000))
+ end
+ for _, timer in ipairs(timers) do
+ local name = timer.name
+ io.write(string.format("%"..nlen.."s %"..tlen..".3f ms for %"..clen.."d", name..":", timer.dt * 1000, timer.count))
+ if timer.count > 1 then
+ io.write(string.format(" (avg: %.3f ms)", timer.dt * 1000 / timer.count))
+ end
+ io.write("\n")
+ assert(timer.started == 0)
+ end
+ print(string.format("%"..nlen.."s %"..tlen..".3f ms", "total:", (t1 - t0) * 1000))
+ print("----------------------------------------------------------------------------")
+end
+
+return perf
\ No newline at end of file
diff --git a/src/printclasses.lua b/src/printclasses.lua
index d0afc95..814c052 100755
--- a/src/printclasses.lua
+++ b/src/printclasses.lua
@@ -8,7 +8,7 @@ local format = string.format
local lwtk = require("lwtk")
local lfs = require("lfs")
-local getSuperClass = lwtk.getSuperClass
+lwtk.NDEBUG = true
local moduleNames = {}
@@ -26,12 +26,6 @@ collect("lwtk/internal")
collect("lwtk/lpugl")
collect("lwtk/love")
-local function getFullPath(c)
- local s = getSuperClass(c)
- local p = s and getFullPath(s)..">" or ""
- return p..c.__name:match("^lwtk%.(.*)$")
-end
-
table.sort(moduleNames)
local maxl = 0
@@ -46,7 +40,7 @@ for i = 1, #moduleNames do
if #n > maxl then
maxl = #n
end
- classpaths[#classpaths + 1] = getFullPath(m)
+ classpaths[#classpaths + 1] = m:getReverseClassPath():gsub("([/#])lwtk%.", "%1")
stylepaths[#stylepaths + 1] = lwtk.get.stylePath[m]
end
end
diff --git a/src/test01.lua b/src/test01.lua
index 4c85afc..e30dbd5 100644
--- a/src/test01.lua
+++ b/src/test01.lua
@@ -62,13 +62,17 @@ local app = lwtk.Application("test01.lua", lwtk.Style {
local MyGroup = lwtk.newClass("MyGroup", lwtk.Group)
+MyGroup:declare("color")
+
local MyBox = lwtk.newClass("MyBox", lwtk.Widget)
+MyBox:declare("color")
+
function MyBox:setColor(color)
self.color = color
end
-function MyBox:onDraw(ctx)
+function MyBox.override:onDraw(ctx)
local c = self:getStyleParam("Color")
if self.id == "mb1" then
--do
@@ -79,14 +83,14 @@ function MyBox:onDraw(ctx)
end
MyGroup.setColor = MyBox.setColor
-MyGroup.onDraw = MyBox.onDraw
+MyGroup.override.onDraw = MyBox.onDraw
-function MyBox:onMouseEnter(x, y)
+function MyBox.override:onMouseEnter(x, y)
-- print(self.id, "move", x, y)
self:setState("hover", true)
end
-function MyBox:onMouseLeave(x, y)
+function MyBox.override:onMouseLeave(x, y)
-- print(self.id, "leave", x, y)
self:setState("hover", false)
end
@@ -120,13 +124,13 @@ local win1 = app:newWindow {
print(win1:childById("b1"))
print(win1:childById("mb1"))
-assert(win1:childById("mb1").__name == "MyBox")
-assert(win1:childById("b1").__name == "lwtk.Button")
-assert(win1:childById("b2").__name == "lwtk.PushButton")
-assert(win1:childById("b2"):is(PushButton))
-assert(win1:childById("b2"):is(Button))
-assert(win1:childById("b1"):is(Button))
-assert(not win1:childById("b1"):is(PushButton))
+assert(lwtk.type(win1:childById("mb1")) == "MyBox")
+assert(lwtk.type(win1:childById("b1")) == "lwtk.Button")
+assert(lwtk.type(win1:childById("b2")) == "lwtk.PushButton")
+assert(win1:childById("b2"):isInstanceOf(PushButton))
+assert(win1:childById("b2"):isInstanceOf(Button))
+assert(win1:childById("b1"):isInstanceOf(Button))
+assert(not win1:childById("b1"):isInstanceOf(PushButton))
local g1a = Group {
id = "g1",
@@ -144,7 +148,7 @@ g:addChild(g1a)
print(g:addChild(Button { id = "b3" }))
print(g:addChild(Button()):getStyleParam"borderColor")
local b = g:addChild(PushButton())
-print(b, b.__name, b.className)
+print(b, lwtk.type(b), getmetatable(b).__name)
b.state.xxx = true
print(b:getStyleParam"borderColor")
win1:show()
diff --git a/src/test03a.lua b/src/test03a.lua
index 2ba5fd4..36254c2 100644
--- a/src/test03a.lua
+++ b/src/test03a.lua
@@ -11,22 +11,24 @@ local newClass = lwtk.newClass
local MyBox = newClass("MyBox", lwtk.Widget)
do
- function MyBox:onDraw(ctx)
+ MyBox:declare("measures")
+
+ function MyBox.implement:onDraw(ctx)
local w, h = self:getSize()
ctx:fillRect(self:getStyleParam("Color"), 0, 0, w, h)
end
- function MyBox:setMeasures(m)
- self.measures = m
- end
- function MyBox:getMeasures()
+ function MyBox.implement:getMeasures()
local m = self.measures
return m[1], m[2], m[3], m[4], m[5], m[6]
end
+ function MyBox:setMeasures(m)
+ self.measures = m
+ end
end
local MyColumn = newClass("MyColumn", Column)
do
- function MyColumn:onDraw(ctx)
+ function MyColumn.override:onDraw(ctx)
local w, h = self:getSize()
ctx:fillRect(self:getStyleParam("Color"), 0, 0, w, h)
end
diff --git a/src/test03b.lua b/src/test03b.lua
index cee5256..bfa8667 100644
--- a/src/test03b.lua
+++ b/src/test03b.lua
@@ -6,18 +6,19 @@ local Widget = lwtk.Widget
local Color = lwtk.Color
local Row = lwtk.Row
local newClass = lwtk.newClass
-local fillRect = lwtk.draw.fillRect
local MyBox = newClass("MyBox", lwtk.Widget)
do
- function MyBox:onDraw(ctx)
+ MyBox:declare("measures")
+
+ function MyBox.implement:onDraw(ctx)
local w, h = self:getSize()
- fillRect(ctx, self:getStyleParam("Color"), 0, 0, w, h)
+ ctx:fillRect(self:getStyleParam("Color"), 0, 0, w, h)
end
function MyBox:setMeasures(m)
self.measures = m
end
- function MyBox:getMeasures()
+ function MyBox.implement:getMeasures()
local m = self.measures
return m[1], m[2], m[3], m[4], m[5], m[6]
end
@@ -25,9 +26,9 @@ end
local MyRow = newClass("MyRow", Row)
do
- function MyRow:onDraw(ctx)
+ function MyRow.implement:onDraw(ctx)
local w, h = self:getSize()
- fillRect(ctx, self:getStyleParam("Color"), 0, 0, w, h)
+ ctx:fillRect(self:getStyleParam("Color"), 0, 0, w, h)
end
end
diff --git a/src/tests/test01.lua b/src/tests/test01.lua
index 5dbe430..a388ec4 100644
--- a/src/tests/test01.lua
+++ b/src/tests/test01.lua
@@ -1,4 +1,5 @@
local lua_version = _VERSION:match("[%d%.]*$")
+local isLua51 = (lua_version == "5.1")
local isOldLua = (#lua_version == 3 and lua_version < "5.3")
-------------------------------------------------------------------------------------------
@@ -28,15 +29,16 @@ end
PRINT("----------------------------------------------------------------------------------")
do
- local M1 = lwtk.newMixin("M1")
- function M1.initClass(M1, Super) -- luacheck: ignore 431/M1
- function M1:new(value)
- Super.new(self, 1000 + value)
+ local M1 = lwtk.newMixin("M1",
+ function(M1, Super)
+ function M1.override:new(value)
+ Super.new(self, 1000 + value)
+ end
+ function M1.override:getValue()
+ return 1000 + Super.getValue(self)
+ end
end
- function M1:getValue()
- return 1000 + Super.getValue(self)
- end
- end
+ )
function M1:getValue2()
return 555
end
@@ -45,12 +47,13 @@ do
assert(not M1:isInstanceOf(lwtk.Object))
assert(not M1:isInstanceOf(lwtk.Class))
assert(lwtk.isInstanceOf(M1, lwtk.Mixin))
- assert(tostring(M1) == "lwtk.Mixin(M1)")
+ assert(tostring(M1) == "lwtk.Mixin")
local C1 = lwtk.newClass("C1")
assert(C1:isInstanceOf(lwtk.Class))
assert(not C1:isInstanceOf(lwtk.Object))
assert(not C1:isInstanceOf(lwtk.Mixin))
assert(lwtk.isInstanceOf(C1, lwtk.Class))
+ C1.value = false
function C1:new(value)
self.value = value + 1
end
@@ -58,7 +61,7 @@ do
return self.value + 1
end
assert(lwtk.type(C1) == "lwtk.Class")
- assert(tostring(C1) == "C1")
+ assert(tostring(C1) == "lwtk.Class")
local c1 = C1(100)
@@ -67,11 +70,8 @@ do
assert(not c1:isInstanceOf(lwtk.Class))
assert(not c1:isInstanceOf(lwtk.Mixin))
assert(lwtk.Object.isInstanceOf(c1, lwtk.Object))
- assert(lwtk.Object.super == nil)
assert(getSuperClass(lwtk.Object) == nil)
- assert(C1.super == nil)
assert(getSuperClass(C1) == lwtk.Object)
- assert(c1.super == nil)
assert(getSuperClass(c1) == nil)
assert(lwtk.type(c1) == "C1")
@@ -81,24 +81,24 @@ do
local M1C1 = M1(C1)
assertEq(lwtk.type(M1C1), "lwtk.Class")
- assertEq(tostring(M1C1), "M1")
+ assertEq(tostring(M1C1), "lwtk.Class")
assertEq(getSuperClass(M1C1), C1)
local M1C1_2 = M1(C1)
assertEq(lwtk.type(M1C1_2), "lwtk.Class")
- assertEq(tostring(M1C1_2), "M1")
+ assertEq(tostring(M1C1_2), "lwtk.Class")
assertEq(getSuperClass(M1C1_2), C1)
assertEq(M1C1_2, M1C1)
local m1c1 = M1C1(100)
- assertEq(lwtk.type(m1c1), "M1")
+ assertEq(lwtk.type(m1c1), "M1(C1)")
assert(m1c1:getValue(), 2102)
assert(m1c1:getValue(), 2102)
assert(m1c1:getValue2(), 555)
local C2 = lwtk.newClass("C2", M1(C1))
assertEq(lwtk.type(C2), "lwtk.Class")
- assertEq(tostring(C2), "C2")
+ assertEq(tostring(C2), "lwtk.Class")
assertEq(getSuperClass(C2), M1C1)
local c2 = C2(200)
@@ -106,17 +106,21 @@ do
assert(c2:getValue(), 2202)
assert(c2:getValue(), 2202)
- local C3 = lwtk.newClass("C3", M1(M1(C1)))
- assertEq(lwtk.type(C3), "lwtk.Class")
- assertEq(tostring(C3), "C3")
- assertEq(lwtk.type(getSuperClass(C3)), "lwtk.Class")
- assertEq(tostring(getSuperClass(C3)), "M1")
- assertEq(getSuperClass(C3), M1(M1(C1)))
-
- local c3 = C3(300)
- assertEq(lwtk.type(c3), "C3")
- assert(c3:getValue(), 2304)
- assert(c3:getValue(), 2304)
+ assert(not pcall(function() lwtk.newClass("C3", M1(M1(C1))) end))
+end
+PRINT("----------------------------------------------------------------------------------")
+do
+ local T = setmetatable({}, { __mode = "v" })
+ T.C1 = lwtk.newClass("C1")
+ collectgarbage()
+ if isLua51 then
+ assert(T.C1 ~= nil)
+ lwtk.undef(T.C1)
+ collectgarbage()
+ assert(T.C1 == nil)
+ else
+ assert(T.C1 == nil)
+ end
end
PRINT("----------------------------------------------------------------------------------")
do
@@ -136,12 +140,13 @@ do
assert(C1.a == "a1")
assert(C1.extra.b == "b1")
assert(c1.a == "a1")
- assert(c1.extra.b == "b1")
+ assert(not pcall(function() return c1.extra end))
+ assert(C1.extra.b == "b1")
assert(C2.a == "a1")
- assert(C2.extra == nil)
+ assert(next(C2.extra) == nil)
assert(c2.a == "a1")
- assert(c2.extra == nil)
+ assert(not pcall(function() return c2.extra end))
local C3 = lwtk.newClass("C3", C1)
C3.extra = {}
@@ -149,53 +154,68 @@ do
local c3 = C3()
assert(c3:m1(2) == 3)
assert(C3.extra.c == "c3")
- assert(c3.extra.c == "c3")
+ assert(not pcall(function() return c3.extra end))
end
+if isLua51 then
+ lwtk.undef("C1") -- for Lua 5.1
+ lwtk.undef("C2") -- for Lua 5.1
+end
+collectgarbage()
PRINT("----------------------------------------------------------------------------------")
do
- local M1 = lwtk.newMixin("M1")
- function M1.initClass(M1, Super) -- luacheck: ignore 431/M1
- end
- local M2 = lwtk.newMixin("M2", M1)
- function M2.initClass(M2, Super) -- luacheck: ignore 431/M2
- end
+ local M1 = lwtk.newMixin("M1",
+ function(M1, Super)
+ end
+ )
+ local M2 = lwtk.newMixin("M2", M1,
+ function(M2, Super)
+ end
+ )
assertEq(lwtk.type(M2), "lwtk.Mixin")
- assertEq(tostring(M2), "lwtk.Mixin(M2)")
+ assertEq(tostring(M2), "lwtk.Mixin")
local C1 = lwtk.newClass("C1")
assertEq(lwtk.type(C1), "lwtk.Class")
local M2C1 = M2(C1)
assertEq(lwtk.type(M2C1), "lwtk.Class")
local C2 = lwtk.newClass("C2", M2C1)
assertEq(lwtk.type(C2), "lwtk.Class")
- assertEq(tostring(C2), "C2")
- assertEq(tostring(getSuperClass(C2)), "M2")
- assertEq(tostring(getSuperClass(getSuperClass(C2))), "M1")
- assertEq(tostring(getSuperClass(getSuperClass(getSuperClass(C2)))), "C1")
+ assertEq(tostring(C2), "lwtk.Class")
+ assertEq(tostring(getSuperClass(C2)), "lwtk.Class")
+ assertEq(tostring(getSuperClass(getSuperClass(C2))), "lwtk.Class")
+ assertEq(tostring(getSuperClass(getSuperClass(getSuperClass(C2)))), "lwtk.Class")
+end
+if isLua51 then
+ lwtk.undef("C1") -- for Lua 5.1
+ lwtk.undef("C2") -- for Lua 5.1
end
+collectgarbage()
PRINT("----------------------------------------------------------------------------------")
do
- local M1 = lwtk.newMixin("M1")
- function M1.initClass(M1, Super) -- luacheck: ignore 431/M1
- end
- local M2 = lwtk.newMixin("M2")
- function M2.initClass(M2, Super) -- luacheck: ignore 431/M2
- end
- local M3 = lwtk.newMixin("M3", M1, M2)
- function M3.initClass(M3, Super) -- luacheck: ignore 431/M3
- end
+ local M1 = lwtk.newMixin("M1",
+ function(M1, Super)
+ end
+ )
+ local M2 = lwtk.newMixin("M2",
+ function(M2, Super)
+ end
+ )
+ local M3 = lwtk.newMixin("M3", M1, M2,
+ function(M3, Super)
+ end
+ )
assertEq(lwtk.type(M3), "lwtk.Mixin")
- assertEq(tostring(M3), "lwtk.Mixin(M3)")
+ assertEq(tostring(M3), "lwtk.Mixin")
local C1 = lwtk.newClass("C1")
assertEq(lwtk.type(C1), "lwtk.Class")
local M3C1 = M3(C1)
assertEq(lwtk.type(M3C1), "lwtk.Class")
local C2 = lwtk.newClass("C2", M3C1)
assertEq(lwtk.type(C2), "lwtk.Class")
- assertEq(tostring(C2), "C2")
- assertEq(tostring(getSuperClass(C2)), "M3")
- assertEq(tostring(getSuperClass(getSuperClass(C2))), "M1")
- assertEq(tostring(getSuperClass(getSuperClass(getSuperClass(C2)))), "M2")
- assertEq(tostring(getSuperClass(getSuperClass(getSuperClass(getSuperClass(C2))))), "C1")
+ assertEq(tostring(C2), "lwtk.Class")
+ assertEq(tostring(getSuperClass(C2)), "lwtk.Class")
+ assertEq(tostring(getSuperClass(getSuperClass(C2))), "lwtk.Class")
+ assertEq(tostring(getSuperClass(getSuperClass(getSuperClass(C2)))), "lwtk.Class")
+ assertEq(tostring(getSuperClass(getSuperClass(getSuperClass(getSuperClass(C2))))), "lwtk.Class")
end
PRINT("----------------------------------------------------------------------------------")
@@ -230,7 +250,7 @@ do
end
PRINT("----------------------------------------------------------------------------------")
do
- assert(tostring(lwtk.Button) == "lwtk.Button")
+ assert(tostring(lwtk.Button) == "lwtk.Class")
assert(tostring(lwtk.Button()):match("^lwtk.Button: [x0-9A-Fa-f]+$"))
local ok, err = pcall(function() lwtk.Button()() end)
@@ -242,13 +262,15 @@ do
local MyButton = lwtk.newClass("MyButton", lwtk.Widget)
do
+ MyButton.text = false
+
function MyButton:setText(text)
self.text = text
self:triggerRedraw()
end
end
- assertEq(tostring(MyButton), "MyButton")
+ assertEq(tostring(MyButton), "lwtk.Class")
assert(tostring(MyButton()):match("^MyButton: [x0-9A-Fa-f]+$"))
local g1 = Group {
@@ -265,7 +287,6 @@ do
assert(g1:childById("b1") ~= nil)
assert(g1:childById("b2") ~= nil)
assertEq(g1:childById("b2").text, "B2")
- assertEq(g1.root, nil)
assertEq(g1:childById("b2"):getRoot(), g1)
assertEq(g1:childById("b2"):getRoot(), g1)
assertEq(g1:childById("b2"):getRoot():childById("b1").text, "B1")
@@ -282,9 +303,6 @@ do
assert (g2:childById("b2") ~= nil)
assertEq(g2:childById("b2"):getRoot(), g2)
- local BorderedButton = lwtk.Bordered(lwtk.Button)
- assert(tostring(BorderedButton) == "lwtk.Bordered(lwtk.Button)")
- assert(tostring(BorderedButton()):match("^lwtk.Bordered%(lwtk.Button%)%: [x0-9A-Fa-f]+$"))
end
PRINT("----------------------------------------------------------------------------------")
do
@@ -385,25 +403,28 @@ do
local MyBox = lwtk.newClass("MyBox", lwtk.Widget)
+ MyBox.color = false
+
function MyBox:setColor(color)
self.color = color
end
- function MyBox:onDraw(ctx)
+ function MyBox.implement:onDraw(ctx)
local c = self:getStyleParam("Color")
ctx:set_source_rgb(c:toRGB())
ctx:rectangle(0, 0, self.w, self.h)
ctx:fill()
end
+ MyGroup.color = false
MyGroup.setColor = MyBox.setColor
- MyGroup.onDraw = MyBox.onDraw
+ MyGroup.implement.onDraw = MyBox.onDraw
- function MyBox:onMouseEnter(x, y)
+ function MyBox.implement:onMouseEnter(x, y)
self:setState("hover", true)
end
- function MyBox:onMouseLeave(x, y)
+ function MyBox.implement:onMouseLeave(x, y)
-- print(self.id, "leave", x, y)
self:setState("hover", false)
end
@@ -443,9 +464,9 @@ do
--local b0 = win1.child.child.xxx
print(win1:childById("b1"))
print(win1:childById("mb1"))
- assert(win1:childById("mb1").__name == "MyBox")
- assert(win1:childById("b1").__name == "lwtk.Button")
- assert(win1:childById("b2").__name == "lwtk.PushButton")
+ assertEq(getmetatable(win1:childById("mb1")).__name, "MyBox")
+ assertEq(getmetatable(win1:childById("b1")).__name, "lwtk.Button")
+ assert(getmetatable(win1:childById("b2")).__name == "lwtk.PushButton")
assert(win1:childById("b2"):isInstanceOf(PushButton))
assert(win1:childById("b2"):isInstanceOf(Button))
assert(win1:childById("b1"):isInstanceOf(Button))
@@ -520,4 +541,17 @@ do
assertEq(g:childById("g1"):byId("g/g2"):childById("b2").text, "g2b2")
end
PRINT("----------------------------------------------------------------------------------")
+do
+ local get = lwtk.StyleRef.get
+ local scale = lwtk.StyleRef.scale
+
+ local s = lwtk.DefaultStyle()
+
+ local w1 = lwtk.Widget { }
+ local w2 = lwtk.Widget { style = { TextSize = scale(100, get"TextSize") } }
+
+ assert(s:getStyleParam(w2, "TextSize") == 100 * s:getStyleParam(w1, "TextSize"))
+
+end
+PRINT("----------------------------------------------------------------------------------")
print("OK.")
diff --git a/src/tests/test02.lua b/src/tests/test02.lua
index 123237e..2eb2f55 100644
--- a/src/tests/test02.lua
+++ b/src/tests/test02.lua
@@ -21,11 +21,11 @@ end
local Box = lwtk.newClass("Box", lwtk.Widget)
-function Box:onDraw(ctx)
+function Box.implement:onDraw(ctx)
print("onDraw", self.id)
end
-function Box:getMeasures()
+function Box.implement:getMeasures()
return 0, 0, 20, 10, 200, 100
end