Hello,
i’d like to share an way to create an context-manager that uses LinearTypes.
Also would appreciate any feedbacks from team on this.
(ContextManager and requiring __copyinit__
or not)
This struct greatly improved the API of an UI i’m working on (by a lot),
while retaining all the benefits that LinearTypes brings.
Example with emojis:
#✏️ Note: the struct have no `Movable` `Copyable` !
struct MoveCursor[
O:MutableOrigin=MutableOrigin.empty,
border:StyledBorder = __StyleBorderNone,
border_color: Fg= Fg.default
]():
var ui: Pointer[UI, O]
var m: StartedMeasurment[O] #LinearType ⚪
var after: Bool #Switcher 🔀
var storage_border: Border[O] #LinearType ⬜
@staticmethod
fn AfterThis[border:StyledBorder = __StyleBorderNone, border_color: Fg= Fg.default](
mut ui:UI,
out ret: MoveCursor[__origin_of(ui), border, border_color]
):
ret = __type_of(ret)(ui)
ret.after=True #Set switch to after ➡️
@staticmethod
fn BelowThis[border:StyledBorder = __StyleBorderNone, border_color: Fg= Fg.default](
mut ui:UI,
out ret: MoveCursor[__origin_of(ui), border, border_color]
):
ret = __type_of(ret)(ui)
ret.after=False #Set switch to below ⬇️
fn __init__(out self, ref[O]ui:UI):
self.ui = Pointer(to=ui)
self.m = ui.start_measuring()
self.after = False #Default switch to ➡️
@parameter
if Self.__has_border[border]():
#Initialize LinearType 🟩 for using it !
self.storage_border = Border[O](self.ui[])
else:
#MarkInitialize LinearType 🟦 for __disable_del() later
#Line is not here to not encourage unsafe things
#Users can just initialize the type with an "Do nothing" parameter
@staticmethod
fn __has_border[b:StyledBorder]()->Bool:
#If type is "none", return False (types-state)
return not _type_is_eq[b, __StyleBorderNone]()
fn __enter__(mut self): ... #Nothing here, struct uses `__init__`
fn __exit__(mut self): ... #Nothing here, struct uses `__del__`
fn __del__(owned self):
@parameter
if Self.__has_border[border]():
#Consume 1 🟩 with struct method that does something
self.storage_border^.end_border[border](self.ui[], border_color)
else:
#Consume 2 🟦 with struct method that __disable_del
self.storage_border^.__unsafe_del()
# ⚪ Consume type into another LinearType (`CompletedMeasurement`)
var tmp = self.m^.stop_measuring()
if self.after: #Check the switch 🔀
tmp^.move_cursor_after() #Consume 1 🟢
else:
tmp^.move_cursor_below() #Consume 2 🔵
The result is this API:
with MoveCursor.BelowThis[StyledBorderSimple](ui):
...
with MoveCursor.BelowThis[StyledBorderSimple, Fg.magenta](ui):
...
with MoveCursor.BelowThis(ui):
...
with MoveCursor.AfterThis[StyledBorderDouble](ui):
...
with MoveCursor.AfterThis[StyledBorderDouble, Fg.magenta](ui):
...
with MoveCursor.AfterThis(ui):
...
And compared to the wrapped API:
var m = ui.start_measuring()
var b = m.start_border() #b now have origin of m
"Hello world" in ui
b^.end_border[StyledBorderSimple, Fg.blue]() #b end before m
var m_stop = m^.stop_measuring()
m_stop^.move_cursor_below() #all measured, can move draw cursor!
Complete example: