Hi y'all!
I'm working on graphics/ui Library for the fantastic picostdlib (actually I'm using the nice rework) Right now I'm pondering on how to organize the widgets and make them simple to use, here is a broken down code of what I'm trying to do:
type
# Concept for display, so that all displays can use the same basic functions for drawing
AnyDisplay*[T] = concept display # T is the type of the color, e.g. for 1 bit displays uint8 and for rgb displays uint32
display.getPixel(int, int) is T
display.setPixel(int, int, T)
display.color is T # this is only used so that T can be infered other places
# implementation for a 1 bit display that uses uint8 for the color
OneBitDisplay* = object
color*: uint8 # this is only used so that the color type can be infered elsewhere
proc getPixel*(disp: OneBitDisplay; x, y: int): uint8 =
discard
proc setPixel*(disp: var OneBitDisplay; x, y: int; color: uint8) =
discard
# check if concept is matched
echo("OneBitDisplay is AnyDisplay: ",OneBitDisplay is AnyDisplay)
type
# Basic Type for widgets that draw on a display
Widget*[T: AnyDisplay] = ref object of RootObj # T is of type AnyDisplay, so that the widget can use the drawing functions
bgcolor*: typeof(T.color)
display: T
proc initWidget*[T](display: T): Widget[T] =
new(result)
result.display = display
proc draw*[T](self: Widget[T]) =
echo("Drawing normal widget")
type
# Label that contains text
Label*[T] = ref object of Widget[T]
text = ""
proc initLabel*[T](display: T, label: string): Label[T] =
new(result)
result.display = display
proc draw*[T](self: Label[T]) =
echo("Drawing Label: ", self.text)
type
# type for handling events, drawing, etc
UserInterface*[T] = object
activeWidget {.cursor.}: Widget[T] # widget which should be drawn
proc setActiveWidget*[T](self: var UserInterface[T], w: Widget[T]) =
self.activeWidget = w
proc draw*[T](self: var UserInterface[T]) =
self.activeWidget.draw()
var
d: OneBitDisplay
w = initWidget(d)
l = initLabel(d, "blubb")
ui: UserInterface[OneBitDisplay]
ui.setActiveWidget(w)
ui.draw()
ui.setActiveWidget(l)
ui.draw()
Of course this does not work as I want it to, the output is:
Drawing normal widget
Drawing normal widget
Usually I would use methods in this case, but they are not available for generic types, so the question is: How would I implement something like this the nim way? (I don't want to use object variants, as there should be an easy way to define new widget types in different places in the code)
Even though there is a warning for generic methods they do work in my experience. Otherwise you could do pointer proc interfaces where you abstract all procs into proc(_: Widget, ....) then inside do like
proc label(str: string): Widget =
Label(
str: str,
drawProc: (proc(widg: Widget, ...) =
let widg = Label(widg))
)
....
Though if you really want to go into the deep end you could do full static dispatch heavily using generics and concepts like I do with https://github.com/beef331/gooey/
Hi Beef,
thanks a lot for your suggestions! Not sure if want to go down the pointer road, but gooey looks like it is what I need! I'll try and rip out its skeleton so I can adapt it :)