summaryrefslogtreecommitdiff
path: root/lib/luavgl/examples
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-11-12 19:14:09 +1100
committerjacqueline <me@jacqueline.id.au>2023-11-12 19:14:09 +1100
commit8a0a167adbf3d9b6f8b6f16aaf20ca39ad5549de (patch)
tree02b6cf23f591915747ec2994381854a79979c4a0 /lib/luavgl/examples
parent8471046a95ab9e00f7d42b56dbbc9ce3e5b424b9 (diff)
downloadtangara-fw-8a0a167adbf3d9b6f8b6f16aaf20ca39ad5549de.tar.gz
Convert the main menu screen to lua lol
Diffstat (limited to 'lib/luavgl/examples')
-rw-r--r--lib/luavgl/examples/analogTime.lua13
-rw-r--r--lib/luavgl/examples/animation.lua61
-rw-r--r--lib/luavgl/examples/assets/hand-hour.pngbin0 -> 7529 bytes
-rw-r--r--lib/luavgl/examples/assets/hand-minute.pngbin0 -> 9600 bytes
-rw-r--r--lib/luavgl/examples/assets/hand-second.pngbin0 -> 5338 bytes
-rw-r--r--lib/luavgl/examples/assets/lvgl-logo.pngbin0 -> 2790 bytes
-rw-r--r--lib/luavgl/examples/assets/second.pngbin0 -> 2140 bytes
-rw-r--r--lib/luavgl/examples/dropdown.lua74
-rw-r--r--lib/luavgl/examples/examples.lua44
-rw-r--r--lib/luavgl/examples/extension.lua14
-rw-r--r--lib/luavgl/examples/flappyBird/bg_day.pngbin0 -> 29684 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/bird1.pngbin0 -> 323 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/bird2.pngbin0 -> 253 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/bird3.pngbin0 -> 250 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/button_play.pngbin0 -> 1375 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/flappyBird.lua765
-rw-r--r--lib/luavgl/examples/flappyBird/land.pngbin0 -> 2660 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/medals.pngbin0 -> 1571 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/pipe_down.pngbin0 -> 3163 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/pipe_up.pngbin0 -> 3235 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/score.pngbin0 -> 536 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/text_game_over.pngbin0 -> 4224 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/title.pngbin0 -> 3217 bytes
-rw-r--r--lib/luavgl/examples/flexLayout.lua27
-rw-r--r--lib/luavgl/examples/font.lua18
-rw-r--r--lib/luavgl/examples/fs.lua59
-rw-r--r--lib/luavgl/examples/group.lua61
-rw-r--r--lib/luavgl/examples/indev.lua61
-rw-r--r--lib/luavgl/examples/keyboard.lua46
-rw-r--r--lib/luavgl/examples/pointer.lua46
-rw-r--r--lib/luavgl/examples/protectedcall.lua36
-rw-r--r--lib/luavgl/examples/roller.lua14
-rw-r--r--lib/luavgl/examples/tests.lua45
-rw-r--r--lib/luavgl/examples/uservalue.lua34
34 files changed, 1418 insertions, 0 deletions
diff --git a/lib/luavgl/examples/analogTime.lua b/lib/luavgl/examples/analogTime.lua
new file mode 100644
index 00000000..375459a9
--- /dev/null
+++ b/lib/luavgl/examples/analogTime.lua
@@ -0,0 +1,13 @@
+local analogTime = lvgl.AnalogTime(nil, {
+ border_width = 0,
+ x = lvgl.HOR_RES() // 2,
+ y = lvgl.VER_RES() // 2,
+ hands = {
+ hour = SCRIPT_PATH .. "/assets/hand-hour.png",
+ minute = SCRIPT_PATH .. "/assets/hand-minute.png",
+ second = SCRIPT_PATH .. "/assets/hand-second.png",
+ },
+ period = 33,
+})
+
+print("analogTime: ", analogTime)
diff --git a/lib/luavgl/examples/animation.lua b/lib/luavgl/examples/animation.lua
new file mode 100644
index 00000000..95ee5f5a
--- /dev/null
+++ b/lib/luavgl/examples/animation.lua
@@ -0,0 +1,61 @@
+local root = lvgl.Object()
+root:set { w = lvgl.HOR_RES(), h = lvgl.VER_RES() }
+
+-- create image on root and set position/img src/etc. properties.
+root:Object():Image {
+ src = SCRIPT_PATH .. "/assets/lvgl-logo.png",
+ x = 0,
+ y = 0,
+ bg_color = 0x004400,
+ pad_all = 0,
+ align = lvgl.ALIGN.CENTER,
+}:Anim {
+ run = true,
+ start_value = 0,
+ end_value = 3600,
+ duration = 2000,
+ repeat_count = 2,
+ path = "bounce",
+ exec_cb = function(obj, value)
+ obj:set {
+ angle = value
+ }
+ end,
+ done_cb = function (anim, obj)
+ print("anim done.: ", anim, "obj:", obj)
+ anim:delete()
+ end
+}
+
+-- second anim example with playback
+local obj = root:Object {
+ bg_color = "#F00000",
+ radius = lvgl.RADIUS_CIRCLE,
+ align = lvgl.ALIGN.LEFT_MID,
+}
+obj:clear_flag(lvgl.FLAG.SCROLLABLE)
+
+--- @type AnimPara
+local animPara = {
+ run = true,
+ start_value = 10,
+ end_value = 50,
+ duration = 1000,
+ playback_delay = 100,
+ playback_time = 500,
+ repeat_count = lvgl.ANIM_REPEAT_INFINITE,
+ path = "ease_in_out",
+}
+
+animPara.exec_cb = function(obj, value)
+ obj:set { size = value }
+end
+
+obj:Anim(animPara)
+
+animPara.end_value = 240
+animPara.exec_cb = function(obj, value)
+ obj:set { x = value }
+end
+
+obj:Anim(animPara)
diff --git a/lib/luavgl/examples/assets/hand-hour.png b/lib/luavgl/examples/assets/hand-hour.png
new file mode 100644
index 00000000..6f5857a9
--- /dev/null
+++ b/lib/luavgl/examples/assets/hand-hour.png
Binary files differ
diff --git a/lib/luavgl/examples/assets/hand-minute.png b/lib/luavgl/examples/assets/hand-minute.png
new file mode 100644
index 00000000..52f6f1ba
--- /dev/null
+++ b/lib/luavgl/examples/assets/hand-minute.png
Binary files differ
diff --git a/lib/luavgl/examples/assets/hand-second.png b/lib/luavgl/examples/assets/hand-second.png
new file mode 100644
index 00000000..9caf4649
--- /dev/null
+++ b/lib/luavgl/examples/assets/hand-second.png
Binary files differ
diff --git a/lib/luavgl/examples/assets/lvgl-logo.png b/lib/luavgl/examples/assets/lvgl-logo.png
new file mode 100644
index 00000000..b032d1b8
--- /dev/null
+++ b/lib/luavgl/examples/assets/lvgl-logo.png
Binary files differ
diff --git a/lib/luavgl/examples/assets/second.png b/lib/luavgl/examples/assets/second.png
new file mode 100644
index 00000000..d09ea5a3
--- /dev/null
+++ b/lib/luavgl/examples/assets/second.png
Binary files differ
diff --git a/lib/luavgl/examples/dropdown.lua b/lib/luavgl/examples/dropdown.lua
new file mode 100644
index 00000000..037f51cb
--- /dev/null
+++ b/lib/luavgl/examples/dropdown.lua
@@ -0,0 +1,74 @@
+-- a dropdown on top left
+local dd = lvgl.Dropdown(nil, {
+ -- note there are two "Orange"
+ options = "Apple\nBanana\nOrange\nCherry\nGrape\nRaspberry\nMelon\nOrange\nLemon\nNuts",
+ symbol = "\xEF\x81\x94",
+ dir = lvgl.DIR.RIGHT,
+ highlight = false,
+ text = nil,
+ align = {
+ type = lvgl.ALIGN.TOP_LEFT,
+ x_ofs = 20,
+ y_ofs = 20,
+ }
+})
+local list = dd:get("list")
+print("get dropdown list: ", list)
+list:set { text_font = lvgl.BUILTIN_FONT.MONTSERRAT_20 }
+
+print("available options:", dd:get("options"))
+
+local cnt = 0
+dd:onevent(lvgl.EVENT.VALUE_CHANGED,
+ ---comment
+ ---@param obj Dropdown
+ ---@param code ObjEventCode
+ function(obj, code)
+ obj:add_option(string.format("Option:%d", cnt), dd:get("option_cnt"))
+ cnt = cnt + 1
+ print(obj:get("selected_str"), "option_cnt" .. obj:get("option_cnt"))
+ end
+)
+
+-- another dropdown on top left
+local dd = lvgl.Dropdown(nil, {
+ options = "Apple\nBanana\nOrange\nCherry\nGrape\nRaspberry\nMelon\nOrange\nLemon\nNuts",
+ symbol = "\xEF\x81\xB7",
+ dir = lvgl.DIR.BOTTOM,
+ text = "SetText",
+ selected = 5,
+ text_font = lvgl.BUILTIN_FONT.MONTSERRAT_20,
+ align = {
+ type = lvgl.ALIGN.BOTTOM_MID,
+ x_ofs = 0,
+ y_ofs = -20,
+ }
+})
+
+
+dd:get("list"):set { text_font = lvgl.Font("montserrat", 24) }
+dd:onevent(lvgl.EVENT.VALUE_CHANGED, function(obj, code)
+ print(obj:get("selected_str") .. ":" .. obj:get("selected"),
+ "dir:" .. obj:get("dir"),
+ "option_index" .. dd:get("option_index", obj:get("selected_str")))
+end)
+
+
+
+lvgl.Timer {
+ period = 1000,
+ cb = function(t)
+ t:delete()
+ dd:open()
+ print("now dd should be opened: " .. (dd:is_open() and "open" or "closed"))
+ end
+}
+
+lvgl.Timer {
+ period = 2000,
+ cb = function(t)
+ t:delete()
+ dd:close()
+ print("now dd should be closed: " .. (dd:is_open() and "open" or "closed"))
+ end
+}
diff --git a/lib/luavgl/examples/examples.lua b/lib/luavgl/examples/examples.lua
new file mode 100644
index 00000000..64e0b631
--- /dev/null
+++ b/lib/luavgl/examples/examples.lua
@@ -0,0 +1,44 @@
+local container = lvgl.Object(nil, {
+ w = lvgl.HOR_RES(),
+ h = lvgl.VER_RES(),
+ bg_color = "#888",
+ bg_opa = lvgl.OPA(100),
+ border_width = 0,
+ radius = 0,
+ flex = {
+ flex_direction = "row",
+ flex_wrap = "wrap"
+ }
+})
+
+print("created container", container)
+
+local function createBtn(parent, name)
+ local root = parent:Object {
+ w = lvgl.SIZE_CONTENT,
+ h = lvgl.SIZE_CONTENT,
+ bg_color = "#ccc",
+ bg_opa = lvgl.OPA(100),
+ border_width = 0,
+ radius = 10,
+ pad_all = 20,
+ }
+
+ root:onClicked(function()
+ container:delete()
+ require(name)
+ end)
+
+ root:Label {
+ text = name,
+ text_color = "#333",
+ align = lvgl.ALIGN.CENTER,
+ }
+end
+
+createBtn(container, "keyboard")
+createBtn(container, "animation")
+createBtn(container, "pointer")
+createBtn(container, "analogTime")
+createBtn(container, "flappyBird/flappyBird")
+createBtn(container, "tests")
diff --git a/lib/luavgl/examples/extension.lua b/lib/luavgl/examples/extension.lua
new file mode 100644
index 00000000..e51f355c
--- /dev/null
+++ b/lib/luavgl/examples/extension.lua
@@ -0,0 +1,14 @@
+-- demo of external widget added to lvgl. See simulator/extension.c
+
+local extension = lvgl.Extension(nil, {
+ border_width = 1,
+ w = lvgl.PCT(20),
+ h = lvgl.PCT(20),
+ align = lvgl.ALIGN.CENTER,
+})
+
+extension:onClicked(function ()
+ print("clicked")
+end)
+
+print("extension: ", extension)
diff --git a/lib/luavgl/examples/flappyBird/bg_day.png b/lib/luavgl/examples/flappyBird/bg_day.png
new file mode 100644
index 00000000..6d24cc26
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/bg_day.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/bird1.png b/lib/luavgl/examples/flappyBird/bird1.png
new file mode 100644
index 00000000..72b202d5
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/bird1.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/bird2.png b/lib/luavgl/examples/flappyBird/bird2.png
new file mode 100644
index 00000000..46050452
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/bird2.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/bird3.png b/lib/luavgl/examples/flappyBird/bird3.png
new file mode 100644
index 00000000..2fc5128d
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/bird3.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/button_play.png b/lib/luavgl/examples/flappyBird/button_play.png
new file mode 100644
index 00000000..ae3d330b
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/button_play.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/flappyBird.lua b/lib/luavgl/examples/flappyBird/flappyBird.lua
new file mode 100644
index 00000000..26018bba
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/flappyBird.lua
@@ -0,0 +1,765 @@
+local lvgl = require("lvgl")
+
+local MOVE_SPEED = 480 / 8000 -- 8s for 480 pixel, pixel per ms
+local PIXEL_PER_METER = 80
+local TOP_Y = 20
+local BOTTOM_Y = 480 - 112
+local PIPE_COUNT = 5
+local PIPE_GAP = 100
+local PIPE_SPACE = 120
+
+-- SCRIPT_PATH is set in simulator/main.c, used to get the abs path of first
+-- lua script lua get called. In this example, SCRIPT_PATH is set to
+-- path of `examples.lua`, flappyBird.lua is called when button is clicked.
+
+local IMAGE_PATH = SCRIPT_PATH
+if not IMAGE_PATH then
+ IMAGE_PATH = "/"
+ print("Note image root path is set to: ", IMAGE_PATH)
+end
+
+IMAGE_PATH = IMAGE_PATH .. "/flappyBird/"
+print("IMAGE_PATH:", IMAGE_PATH)
+
+local function randomY()
+ return math.random(TOP_Y + 30, BOTTOM_Y - 50 - 50)
+end
+
+local function screenCreate(parent)
+ local property = {
+ w = 480,
+ h = 480,
+ bg_opa = 0,
+ border_width = 0,
+ pad_all = 0
+ }
+
+ local scr
+ if parent then
+ scr = parent:Object{
+ w = 480,
+ h = 480,
+ bg_opa = 0,
+ border_width = 0,
+ pad_all = 0
+ }
+ else
+ scr = lvgl.Object(nil, property)
+ end
+ scr:clear_flag(lvgl.FLAG.SCROLLABLE)
+ scr:clear_flag(lvgl.FLAG.CLICKABLE)
+ return scr
+end
+
+local function Image(parent, src)
+ local img = {}
+ img.widget = parent:Image{
+ src = src
+ }
+
+ img.w, img.h = img.widget:get_img_size()
+ if not img.w or not img.h then
+ error("failed to load image: " .. src)
+ end
+ return img
+end
+
+local function ImageScroll(root, src, animSpeed, y)
+ -- image on right
+ local right = Image(root, src).widget
+ right:set{
+ src = src,
+ x = 480,
+ y = y,
+ pad_all = 0
+ }
+
+ local img = Image(root, src).widget
+ img:set{
+ x = 0,
+ y = y,
+ src = src,
+ pad_all = 0
+ }
+
+ img:Anim{
+ run = true,
+ start_value = 0,
+ end_value = -480,
+ time = 480 / animSpeed,
+ repeat_count = lvgl.ANIM_REPEAT_INFINITE,
+ path = "linear",
+ exec_cb = function(obj, value)
+ img:set{
+ x = value
+ }
+
+ right:set{
+ x = value + 480
+ }
+ end
+ }
+
+ img:clear_flag(lvgl.FLAG.CLICKABLE)
+
+ return img
+end
+
+local function Frames(parent, src, fps)
+ local frame = Image(parent, src[1])
+ fps = fps ~= 0 and fps or 25
+
+ frame.src = src
+ frame.len = #src
+ frame.i = 0
+
+ frame.timer = lvgl.Timer {
+ period = 1000 / fps,
+ cb = function(t)
+ frame.widget:set{
+ src = frame.src[frame.i]
+ }
+
+ frame.i = frame.i + 1
+ if frame.i == frame.len then
+ frame.i = 1
+ end
+ end
+ }
+
+ frame.start = function(self)
+ self.timer:resume()
+ end
+
+ frame.pause = function(self)
+ self.timer:pause()
+ end
+
+ return frame
+end
+
+local function Pipe(parent)
+ local up = Image(parent, IMAGE_PATH .. "pipe_up.png")
+ local down = Image(parent, IMAGE_PATH .. "pipe_down.png")
+ local pipe = {
+ up = up.widget,
+ down = down.widget,
+ w = up.w,
+ h = up.h,
+ x = 0,
+ y = 0
+ }
+
+ function pipe:updatePipePos()
+ self.up:set{
+ x = self.x,
+ y = self.y - up.h
+ }
+
+ self.down:set{
+ x = self.x,
+ y = self.y + PIPE_GAP
+ }
+ end
+
+ pipe:updatePipePos()
+ return pipe
+end
+
+local function ObjInfo(x, y, w, h)
+ return {
+ x = x,
+ y = y,
+ w = w,
+ h = h
+ }
+end
+
+local function Pipes(parent)
+ local pipes = {}
+
+ -- add initial pipe
+ for i = 1, PIPE_COUNT do
+ pipes[i] = Pipe(parent)
+ if i == 1 then
+ pipes.w = pipes[i].w -- record pipe size
+ pipes.h = pipes[i].h
+ end
+ end
+
+ local function pipesPosinit()
+ local x = 480;
+ local y = randomY()
+
+ for i = 1, PIPE_COUNT do
+ local pipe = pipes[i]
+ pipe.x = x
+ pipe.y = y
+ pipe:updatePipePos()
+ pipes[i] = pipe
+ x = x + PIPE_SPACE + pipe.w
+ y = randomY()
+ end
+ end
+
+ pipesPosinit()
+
+ pipes.score = 0
+ pipes.last = PIPE_COUNT -- first pipe index in pipes.pipes
+ pipes.totalWidth = (PIPE_COUNT) * (PIPE_SPACE + pipes.w)
+ pipes.birdInfo = ObjInfo(0, 0, 0, 0)
+ pipes.gapInfo = ObjInfo(0, 0, 0, 0)
+
+ function pipes:setObjInfo(x, y, w, h)
+ self.birdInfo.x = x
+ self.birdInfo.y = y
+ if w then
+ self.birdInfo.w = w
+ end
+ if h then
+ self.birdInfo.h = h
+ end
+ end
+
+ local function setGapInfo(x, y, w, h)
+ pipes.gapInfo.x = x
+ pipes.gapInfo.y = y
+ pipes.gapInfo.w = w
+ pipes.gapInfo.h = h
+ end
+
+ pipes.objPassing = -1
+
+ local function isBirdCollision()
+ local bird = pipes.birdInfo
+ local gap = pipes.gapInfo
+
+ -- far left
+ if bird.x + bird.w < gap.x then
+ return false
+ end
+
+ -- far right
+ if bird.x > gap.x + gap.w then
+ return false
+ end
+
+ -- in middle
+
+ if (bird.y > gap.y) and (bird.y + bird.h < gap.y + gap.h) then
+ return false
+ end
+ return true
+ end
+
+ local function moveVirtualX(dx)
+ for i = 1, PIPE_COUNT do
+ local pipe = pipes[i]
+ local newX = pipe.x + dx
+
+ if newX + pipes.w < 0 then
+ newX = newX + pipes.totalWidth
+ pipe.y = randomY()
+ pipes.last = i
+ end
+
+ pipe.x = newX
+ pipe.updatePipePos(pipe)
+ end
+ end
+
+ local function checkScore(i)
+ local pipe = pipes[i]
+
+ local bird = pipes.birdInfo
+ local gap = pipes.gapInfo
+ local passing = pipes.objPassing
+
+ -- far left or right
+ if bird.x + bird.w < gap.x or bird.x > gap.x + gap.w then
+ if passing > 0 and i == passing then
+ pipes.score = pipes.score + 1
+ passing = -1
+ pipes.scoreUpdateCB(pipes.score)
+ end
+ else
+ if passing < 0 then
+ passing = i
+ end
+ end
+
+ pipes.objPassing = passing
+ end
+
+ --- Detect if obj has collision with pipes
+ local function collisionDetect()
+ local first = (pipes.last % PIPE_COUNT) + 1
+ for idx = 0, PIPE_COUNT - 1 do
+ local i = (first + idx - 1) % PIPE_COUNT + 1
+ local pipe = pipes[i]
+ setGapInfo(pipe.x, pipe.y, pipe.w, PIPE_GAP)
+
+ if isBirdCollision() then
+ local bird = pipes.birdInfo
+ if pipes.collisionCB then
+ pipes.collisionCB()
+ end
+ end
+
+ checkScore(i)
+ end
+ end
+
+ pipes.preValue = 0
+ pipes.anim = pipes[1].up:Anim{
+ run = false,
+ start_value = 0,
+ end_value = 480,
+ time = 480 / MOVE_SPEED, -- MOVE_SPEED
+ repeat_count = lvgl.ANIM_REPEAT_INFINITE,
+ path = "linear",
+ exec_cb = function(obj, value)
+ local x = pipes.preValue
+ local d
+ if value < x then
+ d = value + 480 - x
+ else
+ d = value - x
+ end
+ pipes.preValue = value
+ moveVirtualX(-d)
+ collisionDetect()
+ end
+ }
+
+ function pipes:start()
+ self.anim:start()
+ end
+
+ function pipes:stop()
+ self.anim:stop()
+ end
+
+ function pipes:reset()
+ pipesPosinit()
+ pipes.score = 0
+ pipes.preValue = 0
+ pipes.objPassing = -1
+ end
+
+ function pipes:setCollisionCB(collisionCB)
+ self.collisionCB = collisionCB
+ end
+
+ function pipes:setScoreUpdateCB(cb)
+ self.scoreUpdateCB = cb
+ end
+
+ return pipes
+end
+
+local function Bird(parent, birdMovedCB)
+ -- create bird Frame(sprite) in 5FPS
+ local bird = Frames(parent,
+ {IMAGE_PATH .. "bird1.png", IMAGE_PATH .. "bird2.png", IMAGE_PATH .. "bird3.png"}, 5)
+
+ local function birdVarInit()
+ bird.x = 240 - bird.w / 2
+ bird.y = 240 - bird.h / 2
+ bird.widget:set{
+ x = bird.x,
+ y = bird.y
+ }
+
+ bird.head = 0
+ bird.force = 0 -- in unit of m/s^2 rather than N
+ bird.velocity = 0 -- vertical verlocity
+ bird.time = 0 -- time stamp when it updates
+ bird.moving = false
+ end
+
+ birdVarInit()
+
+ bird.setY = function(self)
+ bird.widget:set{
+ y = bird.y
+ }
+ end
+
+ bird.setHead = function(self)
+ bird.widget:set{
+ angle = self.head
+ }
+ end
+
+ bird.applyForce = function(self, force)
+ self.force = force
+ if bird.moving then
+ return
+ end
+
+ bird.moving = true
+ self.y_anim:start()
+ end
+
+ bird.pressed = function(self)
+ bird:applyForce(-13)
+ bird.velocity = 0
+ end
+
+ bird.released = function(self)
+ bird:applyForce(9.8)
+ bird.velocity = 0
+ end
+
+ local function velocity2HeadAngle(v)
+ -- -9.8 ~ 9.8:90 ~ -90
+ return v * 60
+ end
+
+ -- y moving anim, in time.
+ bird.y_anim = bird.widget:Anim{
+ run = false,
+ start_value = 0,
+ end_value = 1000,
+ time = 1000, -- 1000 ms
+ repeat_count = lvgl.ANIM_REPEAT_INFINITE,
+ path = "linear",
+ exec_cb = function(obj, tNow)
+ -- we use anim to get current time, can calculate position based on force/velocity
+ if tNow < bird.time then
+ tNow = tNow + 1000
+ end
+ local y = bird.y
+ local preT = bird.time
+ local v = bird.velocity
+ local t = tNow < preT and tNow + 1000 - preT or tNow - preT
+ t = t * 0.001 -- ms to s
+
+ v = bird.force * t + v
+ if v > 10 then
+ v = 10
+ end
+
+ if v < -10 then
+ v = -10
+ end
+
+ y = y + v * t * PIXEL_PER_METER
+ if y > BOTTOM_Y - 30 then
+ y = BOTTOM_Y - 30
+ v = 0
+ end
+ if y < TOP_Y then
+ y = TOP_Y
+ v = 0
+ end
+
+ bird.y = y
+ bird.time = tNow
+ bird.velocity = v
+ bird.head = velocity2HeadAngle(v)
+
+ birdMovedCB(bird.x, bird.y)
+ -- set y
+ bird:setY()
+ bird:setHead()
+ end
+ }
+
+ function bird:stop()
+ bird.y_anim:stop()
+ end
+
+ function bird:gameOver()
+ -- like it's released forever
+ bird.released()
+ end
+
+ function bird:start()
+ bird.y_anim:start()
+ end
+
+ function bird:reset()
+ bird.stop()
+ birdVarInit()
+ end
+
+ return bird;
+end
+
+local function Background(root, bgEventCB)
+ local bgLayer = screenCreate(root) -- background layer
+ bgLayer:add_flag(lvgl.FLAG.CLICKABLE) -- we accept event here
+
+ local bg = ImageScroll(bgLayer, IMAGE_PATH .. "bg_day.png", MOVE_SPEED * 0.4, 0)
+ local pipes = Pipes(bgLayer)
+ local land = ImageScroll(bgLayer, IMAGE_PATH .. "land.png", MOVE_SPEED, BOTTOM_Y)
+
+ bgLayer:onevent(lvgl.EVENT.PRESSED, function(obj, code)
+ bgEventCB(lvgl.EVENT.PRESSED)
+ end)
+
+ bgLayer:onevent(lvgl.EVENT.RELEASED, function(obj, code)
+ bgEventCB(lvgl.EVENT.RELEASED)
+ end)
+
+ return {
+ pipes = pipes
+ }
+end
+
+local function SysLayer(root)
+ local sysLayer = screenCreate(root) -- upper layer
+ return sysLayer
+end
+
+local function createPlayBtn(sysLayer, onEvent)
+ local playBtn = Image(sysLayer, IMAGE_PATH .. "button_play.png").widget
+ playBtn:add_flag(lvgl.FLAG.CLICKABLE)
+ playBtn:set{
+ align = {
+ type = lvgl.ALIGN.CENTER,
+ y_ofs = 80
+ }
+ }
+
+ playBtn:onevent(lvgl.EVENT.PRESSED, onEvent)
+
+ return playBtn
+end
+
+local function entry()
+ local scr = screenCreate()
+ local bgLayer
+ local mainLayer
+ local sysLayer
+ local bird
+ local pipes
+ local bgEventCB -- background layer pressed/released event
+ local birdMovedCB -- callback when bird position updates
+ local collisionCB -- callback when collision happends
+ local flagRunning = false
+ local gameStart -- API to start game
+ local gameOver -- API to stop game
+ local scoreLabel
+ local scoreBest = 0
+ local scoreNow = 0
+ local debouncing = false
+ -- global event process
+
+ local scoreUpdateCB = function(score)
+ scoreLabel:set{
+ text = string.format("%03d", score)
+ }
+ scoreNow = score
+ end
+
+ print("font:", lvgl.BUILTIN_FONT.MONTSERRAT_26)
+ gameStart = function()
+ if flagRunning then
+ return
+ end
+
+ bird:reset()
+ pipes:reset()
+ pipes:start()
+ bird:start()
+ flagRunning = true
+ scoreNow = 0
+ if scoreLabel then
+ scoreLabel:set{
+ text = string.format("%03d", 0)
+ }
+ end
+ end
+
+ gameOver = function()
+ if not flagRunning then
+ return
+ end
+
+ debouncing = true
+ flagRunning = false
+
+ pipes:stop()
+ bird:gameOver()
+ if scoreNow > scoreBest then
+ scoreBest = scoreNow
+ end
+
+ local gameoverImg = Image(sysLayer, IMAGE_PATH .. "text_game_over.png").widget
+ gameoverImg:set{
+ align = {
+ type = lvgl.ALIGN.TOP_MID,
+ y_ofs = 100
+ }
+ }
+
+ gameoverImg:Anim{
+ run = true,
+ start_value = 0,
+ end_value = 3600,
+ time = 2000,
+ repeat_count = 2,
+ path = "bounce",
+ exec_cb = function(obj, value)
+ obj:set{
+ angle = value
+ }
+ end
+ }
+
+ local scoreImg = Image(sysLayer, IMAGE_PATH .. "score.png").widget
+ scoreImg:set{
+ align = {
+ type = lvgl.ALIGN.CENTER,
+ y_ofs = -20,
+ x_ofs = 0
+ }
+ }
+ scoreImg:Anim{
+ run = true,
+ start_value = 480,
+ end_value = 0,
+ time = 1000,
+ repeat_count = 1,
+ path = "ease_in",
+ exec_cb = function(obj, value)
+ obj:set{
+ align = {
+ type = lvgl.ALIGN.CENTER,
+ x_ofs = value,
+ y_ofs = -20
+ }
+ }
+ end
+ }
+
+ local scoreResultLabel = scoreImg:Label{
+ text = string.format("%03d", scoreNow),
+ text_font = lvgl.BUILTIN_FONT.MONTSERRAT_22,
+ align = {
+ type = lvgl.ALIGN.TOP_MID,
+ x_ofs = 0,
+ y_ofs = 25
+ }
+ }
+
+ local scoreBestLabel = scoreImg:Label{
+ text = string.format("%03d", scoreBest),
+ text_font = lvgl.BUILTIN_FONT.MONTSERRAT_22,
+ align = {
+ type = lvgl.ALIGN.BOTTOM_MID,
+ x_ofs = 0,
+ y_ofs = -5
+ }
+ }
+ scoreNow = 0
+
+ local playBtn;
+ playBtn = createPlayBtn(sysLayer, function(obj, code)
+ if debouncing then
+ return
+ end
+
+ gameStart()
+ playBtn:delete()
+ playBtn = nil
+ gameoverImg:delete()
+ gameoverImg = nil
+ scoreImg:delete()
+ scoreImg = nil
+ end)
+
+ lvgl.Timer {
+ period = 1000,
+ cb = function(t)
+ t:delete()
+ debouncing = false
+ end
+ }
+ end
+
+ bgEventCB = function(event)
+ if not flagRunning then
+ return
+ end
+
+ if event == lvgl.EVENT.PRESSED then
+ bird:pressed()
+ else
+ bird:released()
+ end
+ end
+
+ local birdMovedCB = function(x, y)
+ pipes:setObjInfo(bird.x, bird.y) -- set intial bird position.
+ end
+
+ local collisionCB = function()
+ print("bird collision, stop game")
+ -- call later
+ lvgl.Timer {
+ period = 10,
+ cb = function(t)
+ t:delete()
+ gameOver()
+ end
+ }
+
+ end
+
+ -- background layer, including sky, then pipes and land above
+ bgLayer = Background(scr, bgEventCB) -- background layer
+ pipes = bgLayer.pipes -- get pipes from bg layer for set bird info etc.
+ pipes:setCollisionCB(collisionCB)
+ pipes:setScoreUpdateCB(scoreUpdateCB)
+
+ -- main layer, the bird
+ mainLayer = screenCreate(scr) -- main layer
+ bird = Bird(mainLayer, birdMovedCB)
+ pipes:setObjInfo(bird.x, bird.y, bird.w, bird.h)
+ -- system layer, score etc.
+ sysLayer = SysLayer(scr)
+
+ local title = Image(sysLayer, IMAGE_PATH .. "title.png").widget
+ title:set{
+ align = {
+ type = lvgl.ALIGN.TOP_MID,
+ y_ofs = 80
+ }
+ }
+
+ local playBtn;
+ playBtn = createPlayBtn(sysLayer, function()
+ print("pressed")
+ gameStart()
+ playBtn:delete()
+ playBtn = nil
+ title:delete()
+ title = nil
+
+ local medal = Image(sysLayer, IMAGE_PATH .. "medals.png").widget
+ medal:set{
+ align = {
+ type = lvgl.ALIGN.TOP_MID,
+ y_ofs = 10,
+ x_ofs = -50
+ }
+ }
+ scoreLabel = sysLayer:Label{
+ text = " 000",
+ text_font = lvgl.BUILTIN_FONT.MONTSERRAT_28,
+ align = {
+ type = lvgl.ALIGN.TOP_MID,
+ x_ofs = 10,
+ y_ofs = 20
+ }
+ }
+ end)
+end
+
+entry()
+
+-- bird = Bird(, nil)
diff --git a/lib/luavgl/examples/flappyBird/land.png b/lib/luavgl/examples/flappyBird/land.png
new file mode 100644
index 00000000..b93a861a
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/land.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/medals.png b/lib/luavgl/examples/flappyBird/medals.png
new file mode 100644
index 00000000..c440df81
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/medals.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/pipe_down.png b/lib/luavgl/examples/flappyBird/pipe_down.png
new file mode 100644
index 00000000..e02d6946
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/pipe_down.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/pipe_up.png b/lib/luavgl/examples/flappyBird/pipe_up.png
new file mode 100644
index 00000000..6e8e8d5d
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/pipe_up.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/score.png b/lib/luavgl/examples/flappyBird/score.png
new file mode 100644
index 00000000..cf567b0c
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/score.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/text_game_over.png b/lib/luavgl/examples/flappyBird/text_game_over.png
new file mode 100644
index 00000000..b8c30fc2
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/text_game_over.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/title.png b/lib/luavgl/examples/flappyBird/title.png
new file mode 100644
index 00000000..557340f2
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/title.png
Binary files differ
diff --git a/lib/luavgl/examples/flexLayout.lua b/lib/luavgl/examples/flexLayout.lua
new file mode 100644
index 00000000..afafa5c8
--- /dev/null
+++ b/lib/luavgl/examples/flexLayout.lua
@@ -0,0 +1,27 @@
+local lvgl = require("lvgl")
+
+local root = lvgl.Object(nil, {
+ flex = {
+ flex_direction = "row",
+ flex_wrap = "wrap",
+ justify_content = "center",
+ align_items = "center",
+ align_content = "center",
+ },
+ w = lvgl.HOR_RES(),
+ h = lvgl.VER_RES(),
+ align = lvgl.ALIGN.CENTER
+})
+
+for i = 1, 10 do
+ local item = root:Object {
+ w = 100,
+ h = lvgl.PCT(100),
+ }
+ item:clear_flag(lvgl.FLAG.SCROLLABLE)
+
+ local label = item:Label {
+ text = string.format("label %d", i)
+ }
+ label:center()
+end
diff --git a/lib/luavgl/examples/font.lua b/lib/luavgl/examples/font.lua
new file mode 100644
index 00000000..7d650cc0
--- /dev/null
+++ b/lib/luavgl/examples/font.lua
@@ -0,0 +1,18 @@
+local label1 = lvgl.Label(nil, {
+ x = 0, y = 0,
+ text_font = lvgl.Font("montserrat", 32, "normal"),
+ text = "Hello Font",
+ align = lvgl.ALIGN.CENTER
+})
+
+print("label1: ", label1)
+
+lvgl.Label(nil, {
+ -- x= 0, y = 0,
+ -- text_font = lvgl.Font("MiSansW medium, montserrat", 24),
+ text_font = lvgl.BUILTIN_FONT.MONTSERRAT_22,
+ text = "Hello Font2",
+}):align_to({
+ type = lvgl.ALIGN.OUT_BOTTOM_LEFT,
+ base = label1,
+})
diff --git a/lib/luavgl/examples/fs.lua b/lib/luavgl/examples/fs.lua
new file mode 100644
index 00000000..6672f4d0
--- /dev/null
+++ b/lib/luavgl/examples/fs.lua
@@ -0,0 +1,59 @@
+local function fs_example()
+ -- local f <close>, msg, code = lvgl.open_file(SCRIPT_PATH .. "/tmp.txt") -- for lua 5.4
+ local f, msg, code = lvgl.fs.open_file(SCRIPT_PATH .. "/tmp.txt", "rw")
+ if not f then
+ print("failed: ", msg, code)
+ return
+ end
+
+ print("f: ", f)
+
+ f:write("0123456789", 123, "\n", "the remaining text")
+ f:seek("set", 0) -- go back
+ local header, remaining = f:read(10, "*a")
+ f:close()
+ if not header then
+ print("read failed or EOF")
+ return
+ end
+
+ print("header len:", #header, ": ", header)
+ print("remaining: ", remaining)
+
+ lvgl.Label(nil, {
+ x = 0,
+ y = 0,
+ text_font = lvgl.Font("montserrat", 20, "normal"),
+ text = header .. remaining,
+ align = lvgl.ALIGN.TOP_LEFT
+ })
+
+ local list = lvgl.List(nil, {
+ align = lvgl.ALIGN.TOP_RIGHT,
+ pad_all = 10,
+ text_font = lvgl.BUILTIN_FONT.MONTSERRAT_12
+ })
+ list:add_text("Directory list:")
+
+ -- local dir <close>, msg, code = lvgl.fs.open_dir(SCRIPT_PATH .. "/")
+ local dir, msg, code = lvgl.fs.open_dir(SCRIPT_PATH .. "/")
+ if not dir then
+ print("open dir failed: ", msg, code)
+ return
+ end
+
+ while true do
+ local d = dir:read()
+ if not d then break end
+ local is_dir = string.byte(d, 1) == string.byte("/", 1)
+ local str = (is_dir and "dir: " or "file: ") .. d
+ print(str)
+
+ list:add_text(str):set{
+ border_width = 1,
+ }
+ end
+ dir:close()
+end
+
+fs_example()
diff --git a/lib/luavgl/examples/group.lua b/lib/luavgl/examples/group.lua
new file mode 100644
index 00000000..f80dd325
--- /dev/null
+++ b/lib/luavgl/examples/group.lua
@@ -0,0 +1,61 @@
+local function group_example()
+ local g = lvgl.group.create()
+ g:set_default()
+
+ -- for demo purpose, set all indev to use this group
+ local indev = nil
+ while true do
+ indev = lvgl.indev.get_next(indev)
+ if not indev then break end
+
+ local t = indev:get_type()
+ if t == 2 or t == 4 then
+ indev:set_group(g)
+ end
+ end
+
+ local style = lvgl.Style({
+ border_width = 5,
+ border_color = "#a00",
+ })
+
+ local root = lvgl.Object(nil, {
+ w = lvgl.PCT(100),
+ h = lvgl.PCT(100),
+ align = lvgl.ALIGN.CENTER,
+ bg_color = "#aaa",
+ flex = {
+ flex_direction = "row",
+ flex_wrap = "wrap"
+ }
+ })
+
+ root:add_style(style, lvgl.STATE.FOCUSED)
+
+ for _ = 1, 5 do
+ local obj = root:Object({
+ w = lvgl.PCT(50),
+ h = lvgl.PCT(50),
+ bg_color = "#555",
+ })
+
+ obj:add_style(style, lvgl.STATE.FOCUSED)
+
+ obj:onClicked(function(obj, code)
+ print("clicked: ", obj)
+ end)
+
+ obj:onevent(lvgl.EVENT.FOCUSED, function(obj, code)
+ print("focused: ", obj)
+ obj:scroll_to_view(true)
+ end)
+
+ obj:onevent(lvgl.EVENT.DEFOCUSED, function(obj, code)
+ print("defocused: ", obj)
+ end)
+
+ g:add_obj(obj)
+ end
+end
+
+group_example()
diff --git a/lib/luavgl/examples/indev.lua b/lib/luavgl/examples/indev.lua
new file mode 100644
index 00000000..563b0f00
--- /dev/null
+++ b/lib/luavgl/examples/indev.lua
@@ -0,0 +1,61 @@
+local function indev_example()
+ local indev = lvgl.indev.get_act()
+ print("act indev: ", indev)
+
+
+ indev:on_event(lvgl.EVENT.SHORT_CLICKED, function ()
+ print("indev pressed")
+ end)
+ local obj_act = lvgl.indev.get_obj_act()
+ print("obj_act: ", obj_act)
+
+ local root = lvgl.Object(nil, {
+ w = lvgl.PCT(30),
+ h = lvgl.PCT(30),
+ align = lvgl.ALIGN.CENTER,
+ bg_color = "#aaa",
+ })
+
+ root:Object({
+ w = 1000,
+ h = 1000,
+ align = lvgl.ALIGN.CENTER,
+ bg_color = "#555",
+ }):onevent(lvgl.EVENT.ALL, function(obj, code)
+ local indev = lvgl.indev.get_act()
+
+ if not indev then return end
+
+ if code == lvgl.EVENT.PRESSED then
+ local x, y = indev:get_point()
+ print("pressed: ", x, y)
+ end
+
+ if code == lvgl.EVENT.PRESSING then
+ local x, y = indev:get_vect()
+ print("vect: ", x, y)
+ end
+
+ if code == lvgl.EVENT.GESTURE then
+ local gesture_dir = indev:get_gesture_dir()
+ print("gesture_dir: ", gesture_dir)
+ end
+
+ if code == lvgl.EVENT.RELEASED then
+ local scroll_dir = indev:get_scroll_dir()
+ print("scroll_dir: ", scroll_dir)
+
+ local scroll_obj = indev:get_scroll_obj()
+ print("scroll_obj: ", scroll_obj)
+
+ local x, y = indev:get_vect()
+ local obj_search = obj:indev_search(x, y)
+ print("indev search obj: ", obj_search)
+
+ local obj_search = obj:indev_search({x, y})
+ print("indev search obj2: ", obj_search)
+ end
+ end)
+end
+
+indev_example()
diff --git a/lib/luavgl/examples/keyboard.lua b/lib/luavgl/examples/keyboard.lua
new file mode 100644
index 00000000..feb85d32
--- /dev/null
+++ b/lib/luavgl/examples/keyboard.lua
@@ -0,0 +1,46 @@
+local root = lvgl.Object(nil, {
+ w = lvgl.HOR_RES(),
+ h = lvgl.VER_RES(),
+ pad_all = 0,
+ bg_color = "#333",
+ bg_opa = lvgl.OPA(50),
+})
+root:add_flag(lvgl.FLAG.CLICKABLE)
+
+local ta = root:Textarea {
+ password_mode = false,
+ one_line = true,
+ text_font = lvgl.Font("montserrat", 22),
+ text_color = "#FFF",
+ text = "Input text here",
+ w = lvgl.SIZE_CONTENT,
+ h = lvgl.SIZE_CONTENT,
+ bg_opa = 0,
+ border_width = 2,
+ pad_all = 2,
+ align = lvgl.ALIGN.TOP_MID,
+}
+
+print("created textarea: ", ta)
+
+local keyboard = root:Keyboard {
+ textarea = ta,
+ align = lvgl.ALIGN.BOTTOM_MID,
+}
+print("created keyboard: ", keyboard)
+
+ta:onevent(lvgl.EVENT.PRESSED, function(obj, code)
+ keyboard:clear_flag(lvgl.FLAG.HIDDEN)
+end)
+
+ta:onevent(lvgl.EVENT.READY, function()
+ keyboard:add_flag(lvgl.FLAG.HIDDEN)
+end)
+
+ta:onevent(lvgl.EVENT.CANCEL, function(obj, code)
+ keyboard:add_flag(lvgl.FLAG.HIDDEN)
+end)
+
+root:onClicked(function (obj, code)
+ keyboard:add_flag(lvgl.FLAG.HIDDEN)
+end)
diff --git a/lib/luavgl/examples/pointer.lua b/lib/luavgl/examples/pointer.lua
new file mode 100644
index 00000000..c344f77b
--- /dev/null
+++ b/lib/luavgl/examples/pointer.lua
@@ -0,0 +1,46 @@
+local root = lvgl.Object(nil, {
+ w = 0,
+ h = 0,
+ pad_all = 0,
+ align = lvgl.ALIGN.CENTER,
+ border_width = 1,
+ border_color = "#F00",
+})
+
+local pointer = root:Pointer {
+ src = SCRIPT_PATH .. "/assets/second.png",
+ x = -20,
+ y = -233,
+ border_width = 1,
+ border_color = "#0F0",
+ range = {
+ valueStart = 0,
+ valueRange = 60000,
+ angleStart = 0,
+ angleRange = -3600
+ },
+}
+
+-- lvgl requires src set firstly, since lua cannot guarantee table order,
+-- we set pivot after image src is set.
+pointer:set {
+ pivot = { x = 20, y = 233 },
+}
+
+print("img w,h: ", pointer:get_img_size())
+
+pointer:Anim{
+ run = true,
+ start_value = 0,
+ end_value = 60000,
+ duration = 60000,
+ path = "linear",
+ repeat_count = lvgl.ANIM_REPEAT_INFINITE,
+ exec_cb = function(obj, value)
+ obj:set {
+ value = value
+ }
+ end,
+}
+
+print("created pointer: ", pointer)
diff --git a/lib/luavgl/examples/protectedcall.lua b/lib/luavgl/examples/protectedcall.lua
new file mode 100644
index 00000000..a23fd9db
--- /dev/null
+++ b/lib/luavgl/examples/protectedcall.lua
@@ -0,0 +1,36 @@
+
+local function testCrash()
+ print(nil .. "")
+end
+
+local function testCrash2()
+ testCrash()
+end
+local function testCrash3()
+ testCrash2()
+end
+local function testCrash4()
+ testCrash3()
+end
+
+local function testCrash5()
+ testCrash4()
+end
+
+local function testCrash6()
+ testCrash5()
+end
+
+lvgl.Label(nil, {
+ text = "crash in 1 second.",
+ align = lvgl.ALIGN.CENTER
+})
+
+lvgl.Timer {
+ period = 1000,
+ cb = function(t)
+ t:delete()
+ -- crash in callback function is protected.
+ testCrash6()
+ end
+} \ No newline at end of file
diff --git a/lib/luavgl/examples/roller.lua b/lib/luavgl/examples/roller.lua
new file mode 100644
index 00000000..0d763327
--- /dev/null
+++ b/lib/luavgl/examples/roller.lua
@@ -0,0 +1,14 @@
+local roller = lvgl.Roller(nil, {
+ options = {
+ options = "January\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember",
+ mode = lvgl.ROLLER_MODE.INFINITE,
+ },
+ visible_cnt = 4,
+ align = lvgl.ALIGN.CENTER
+})
+
+print("create roller:", roller)
+roller:onevent(lvgl.EVENT.VALUE_CHANGED, function (obj, code)
+ print(obj:get_selected_str())
+end)
+
diff --git a/lib/luavgl/examples/tests.lua b/lib/luavgl/examples/tests.lua
new file mode 100644
index 00000000..88d224ff
--- /dev/null
+++ b/lib/luavgl/examples/tests.lua
@@ -0,0 +1,45 @@
+local container = lvgl.Object(nil, {
+ w = lvgl.HOR_RES(),
+ h = lvgl.VER_RES(),
+ bg_color = "#888",
+ bg_opa = lvgl.OPA(100),
+ border_width = 0,
+ radius = 0,
+ flex = {
+ flex_direction = "row",
+ flex_wrap = "wrap"
+ }
+})
+
+local function createBtn(parent, name)
+ local root = parent:Object {
+ w = lvgl.SIZE_CONTENT,
+ h = lvgl.SIZE_CONTENT,
+ bg_color = "#ccc",
+ bg_opa = lvgl.OPA(100),
+ border_width = 0,
+ radius = 10,
+ pad_all = 20,
+ }
+
+ root:onClicked(function()
+ container:delete()
+ require(name)
+ end)
+
+ root:Label {
+ text = name,
+ text_color = "#333",
+ align = lvgl.ALIGN.CENTER,
+ }
+end
+
+createBtn(container, "font")
+createBtn(container, "uservalue")
+createBtn(container, "roller")
+createBtn(container, "dropdown")
+createBtn(container, "extension")
+createBtn(container, "fs")
+createBtn(container, "indev")
+createBtn(container, "group")
+createBtn(container, "protectedcall")
diff --git a/lib/luavgl/examples/uservalue.lua b/lib/luavgl/examples/uservalue.lua
new file mode 100644
index 00000000..414d8f0b
--- /dev/null
+++ b/lib/luavgl/examples/uservalue.lua
@@ -0,0 +1,34 @@
+collectgarbage("collect")
+print("initial: ", collectgarbage("count"))
+
+local str_t = {}
+for i = 1, 1024 * 1024 do
+ str_t[#str_t + 1] = 'a'
+end
+
+local str = table.concat(str_t)
+str_t = nil
+
+collectgarbage("collect")
+collectgarbage("collect")
+print("after string collect: ", collectgarbage("count"))
+
+for i = 1, 10 do
+ local label = lvgl.Label(nil)
+ label:set_text_static(str)
+ label:delete()
+end
+str = nil
+
+print("after set text: ", collectgarbage("count"))
+collectgarbage("collect")
+print("after collect: ", collectgarbage("count"))
+print("again: ", collectgarbage("count"))
+collectgarbage("collect")
+print("again2: ", collectgarbage("count"))
+collectgarbage("collect")
+print("again3: ", collectgarbage("count"))
+
+local label = lvgl.Label(nil)
+label:set({ text = "Test Done" })
+label:center()