Tasmota/lib/libesp32/Berry/default/embedded/lvgl_glob.be

257 lines
9.6 KiB
Plaintext

#- embedded class for LVGL globals -#
#- This class stores all globals used by LVGL and cannot be stored in the solidified module -#
#- this limits the globals to a single value '_lvgl' -#
class LVGL_glob
# all variables are lazily initialized to reduce the memory pressure. Until they are used, they consume zero memory
var cb_obj # map between a native C pointer (as int) and the corresponding lv.lv_* berry object, also helps marking the objects as non-gc-able
var cb_event_closure # mapping for event closures per LVGL native pointer (int)
var event_cb # native callback for lv.lv_event
#- below are native callbacks mapped to a closure to a method of this instance -#
var null_cb # cb called if type is not supported
var widget_ctor_cb
var widget_dtor_cb
var widget_event_cb
var widget_struct_default
var widget_struct_by_class
#- this is the fallback callback, if the event is unknown or unsupported -#
static cb_do_nothing = def() print("LVG: call to unsupported callback") end
#- register an lv.lv_* object in the mapping -#
def register_obj(obj)
if self.cb_obj == nil self.cb_obj = {} end
var native_ptr = int(obj._p)
self.cb_obj[native_ptr] = obj
end
def get_object_from_ptr(ptr)
if self.cb_obj != nil
return self.cb_obj.find(ptr) # raise an exception if something is wrong
end
end
def lvgl_event_dispatch(event_ptr)
import introspect
var event = lv.lv_event(introspect.toptr(event_ptr))
var target = event.target
var f = self.cb_event_closure[target]
var obj = self.get_object_from_ptr(target)
#print('>> lvgl_event_dispatch', f, obj, event)
f(obj, event)
end
def gen_cb(name, f, obj, ptr)
#print('>> gen_cb', name, obj, ptr)
# record the object, whatever the callback
if name == "lv_event_cb"
if self.cb_event_closure == nil self.cb_event_closure = {} end
if self.event_cb == nil self.event_cb = tasmota.gen_cb(/ event_ptr -> self.lvgl_event_dispatch(event_ptr)) end # encapsulate 'self' in closure
self.register_obj(obj)
self.cb_event_closure[ptr] = f
return self.event_cb
# elif name == "<other_cb>"
else
if self.null_cb == nil self.null_cb = tasmota.gen_cb(self.cb_do_nothing) end
return self.null_cb
end
end
def widget_ctor_impl(cl_ptr, obj_ptr)
import introspect
var cl = lv.lv_obj_class(cl_ptr)
var obj = self.get_object_from_ptr(obj_ptr)
if self.cb_obj.find(obj) obj = self.cb_obj[obj] end
# print("widget_ctor_impl", cl, obj)
if type(obj) == 'instance' && introspect.get(obj, 'widget_constructor')
obj.widget_constructor(cl)
end
end
def widget_dtor_impl(cl_ptr, obj_ptr)
import introspect
var cl = lv.lv_obj_class(cl_ptr)
var obj = self.get_object_from_ptr(obj_ptr)
# print("widget_dtor_impl", cl, obj)
if type(obj) == 'instance' && introspect.get(obj, 'widget_destructor')
obj.widget_destructor(cl)
end
end
def widget_event_impl(cl_ptr, e_ptr)
import introspect
var cl = lv.lv_obj_class(cl_ptr)
var event = lv.lv_event(e_ptr)
var obj_ptr = event.target
var obj = self.get_object_from_ptr(int(obj_ptr))
if type(obj) == 'instance' && introspect.get(obj, 'widget_event')
obj.widget_event(cl, event)
end
# print("widget_event_impl", cl, obj_ptr, obj, event)
end
def widget_cb()
if self.widget_ctor_cb == nil self.widget_ctor_cb = tasmota.gen_cb(/ cl, obj -> self.widget_ctor_impl(cl, obj)) end
if self.widget_dtor_cb == nil self.widget_dtor_cb = tasmota.gen_cb(/ cl, obj -> self.widget_dtor_impl(cl, obj)) end
if self.widget_event_cb == nil self.widget_event_cb = tasmota.gen_cb(/ cl, e -> self.widget_event_impl(cl, e)) end
if self.widget_struct_default == nil
self.widget_struct_default = lv.lv_obj_class(lv.lv_obj._class).copy()
self.widget_struct_default.base_class = lv.lv_obj._class # by default, inherit from base class `lv_obj`, this can be overriden
self.widget_struct_default.constructor_cb = self.widget_ctor_cb # set the berry cb dispatchers
self.widget_struct_default.destructor_cb = self.widget_dtor_cb
self.widget_struct_default.event_cb = self.widget_event_cb
end
end
#- deregister_obj all information linked to a specific LVGL native object (int) -#
def deregister_obj(obj)
if self.cb_obj != nil self.cb_obj.remove(obj) end
if self.cb_event_closure != nil self.cb_event_closure.remove(obj) end
end
#- initialize a custom widget -#
#- arg must be a subclass of lv.lv_obj -#
def create_custom_widget(obj, parent)
import introspect
if !isinstance(obj, lv.lv_obj) raise "value_error", "arg must be a subclass of lv_obj" end
if self.widget_struct_by_class == nil self.widget_struct_by_class = {} end
var obj_classname = classname(obj)
var obj_class_struct = self.widget_struct_by_class.find(obj_classname)
# print("classname=",obj_classname,"_class",super(obj)._class)
#- not already built, create a new one for this class -#
if obj_class_struct == nil
self.widget_cb() # set up all structures
obj_class_struct = self.widget_struct_default.copy() # get a copy of the structure with pre-defined callbacks
obj_class_struct.base_class = super(obj)._class
if introspect.get(obj, 'widget_width_def') obj_class_struct.width_def = obj.widget_width_def end
if introspect.get(obj, 'widget_height_def') obj_class_struct.height_def = obj.widget_height_def end
if introspect.get(obj, 'widget_editable') obj_class_struct.editable = obj.widget_editable end
if introspect.get(obj, 'widget_group_def') obj_class_struct.group_def = obj.widget_group_def end
if introspect.get(obj, 'widget_instance_size') obj_class_struct.instance_size = obj.widget_instance_size end
#- keep a copy of the structure to avoid GC and reuse if needed -#
self.widget_struct_by_class[obj_classname] = obj_class_struct
end
var lv_obj_ptr = lv.obj_class_create_obj(obj_class_struct, parent)
obj._p = lv_obj_ptr._p
self.register_obj(obj)
obj.class_init_obj()
end
end
_lvgl = LVGL_glob()
# class lv_custom_widget : lv.lv_obj
# # static widget_width_def
# # static widget_height_def
# # static widget_editable
# # static widget_group_def
# # static widget_instance_size
# #
# var percentage # value to display, range 0..100
# var p1, p2, area, line_dsc # instances of objects kept to avoid re-instanciating at each call
# def init(parent)
# _lvgl.create_custom_widget(self, parent)
# # own values
# self.percentage = 100
# # pre-allocate buffers
# self.p1 = lv.lv_point()
# self.p2 = lv.lv_point()
# self.area = lv.lv_area()
# self.line_dsc = lv.lv_draw_line_dsc()
# end
# # def widget_constructor(cl)
# # print("widget_constructor", cl)
# # end
# # def widget_destructor(cl)
# # print("widget_destructor", cl)
# # end
# def widget_event(cl, event)
# var res = lv.obj_event_base(cl, event)
# if res != lv.RES_OK return end
# def atleast1(x) if x >= 1 return x else return 1 end end
# # the model is that we have 4 bars and inter-bar (1/4 of width)
# var height = self.get_height()
# var width = self.get_width()
# var inter_bar = atleast1(width / 15)
# var bar = atleast1((width - inter_bar * 3) / 4)
# var bar_offset = bar / 2
# var code = event.code
# if code == lv.EVENT_DRAW_MAIN
# var clip_area = lv.lv_area(event.param)
# print("widget_event DRAW", clip_area.tomap())
# # lv.event_send(self, lv.EVENT_DRAW_MAIN, clip_area)
# # get coordinates of object
# self.get_coords(self.area)
# var x_ofs = self.area.x1
# var y_ofs = self.area.y1
# lv.draw_line_dsc_init(self.line_dsc) # initialize lv.lv_draw_line_dsc structure
# self.init_draw_line_dsc(lv.PART_MAIN, self.line_dsc)
# self.line_dsc.round_start = 1
# self.line_dsc.round_end = 1
# self.line_dsc.width = bar
# var on_color = self.get_style_line_color(lv.PART_MAIN | lv.STATE_DEFAULT)
# var off_color = self.get_style_bg_color(lv.PART_MAIN | lv.STATE_DEFAULT)
# lv.event_send(self, lv.EVENT_DRAW_PART_BEGIN, self.line_dsc)
# for i:0..3 # 4 bars
# self.line_dsc.color = self.percentage >= (i+1)*20 ? on_color : off_color
# self.p1.y = y_ofs + height - 1 - bar_offset
# self.p1.x = x_ofs + i * (bar + inter_bar) + bar_offset
# self.p2.y = y_ofs + ((3 - i) * (height - bar)) / 4 + bar_offset
# self.p2.x = self.p1.x
# lv.draw_line(self.p1, self.p2, clip_area, self.line_dsc)
# end
# lv.event_send(self, lv.EVENT_DRAW_PART_END, self.line_dsc)
# end
# end
# def set_percentage(v)
# var old_bars = self.percentage / 5
# if v > 100 v = 100 end
# if v < 0 v = 0 end
# self.percentage = v
# if old_bars != v / 5
# self.invalidate() # be frugal and avoid updating the widget if it's not needed
# end
# end
# def get_percentage()
# return self.percentage
# end
# end
# ########## ########## ########## ########## ########## ########## ########## ##########
# lv.start()
# hres = lv.get_hor_res() # should be 320
# vres = lv.get_ver_res() # should be 240
# scr = lv.scr_act() # default screean object
# f20 = lv.montserrat_font(20) # load embedded Montserrat 20
# scr.set_style_bg_color(lv.lv_color(0x0000A0), lv.PART_MAIN | lv.STATE_DEFAULT)
# w = lv_custom_widget(scr)