#!/bin/kuroko from _yutani2 import (YutaniCtx, Font, rgb, rgb, MenuBar, decor_get_bounds, decor_render, MenuList, MenuEntry, MenuEntrySeparator, Message, decor_handle_event, decor_show_default_menu, MenuEntryCustom) from yutani_mainloop import Window, yctx as y, AsyncMainloop, Task, sleep import math import random let mainloop = AsyncMainloop() def close_enough(me): return me.command == 2 and math.sqrt((me.new_x - me.old_x) ** 2 + (me.new_y - me.old_y) **2) < 10 class Button: ''' Basic button widget based on draw_button ''' def __init__(self, window, x, y, width, height, title, callback): self.ctx = window self.x = x self.y = y self.width = width self.height = height self.title = title self.state = 0 self.callback = asyncCallback def __contains__(self, coord): if not isinstance(cord, tuple): return False def focus_enter(self): self.state = 1 return True def focus_leave(self): self.state = 0 return True def mouse_down(self, msg): self.state = 2 return True def mouse_up(self, msg): # TODO check if old_x, old_y is in button self.callback(self) self.state = 0 return True def draw(self): draw_button(self.ctx, self.x, self.y, self.width, self.height, self.title, self.state) class MyMenuWidget(MenuEntryCustom): def __init__(self): super().__init__() self.height = 30 self.rwidth = 148 self.dejavu = Font("sans-serif", 13) self.x = 0 self.y = 0 def render(self, ctx, offset): self.offset = offset if self.hilight: ctx.rect(1,offset,self.width-2,self.height,rgb(100,int(255 * self.x / self.width),int(255 * (self.y-offset) / self.height)),radius=4) self.dejavu.draw_string(ctx, f"{self.x=},{self.y=}", 2, offset + 13) else: self.dejavu.draw_string(ctx, f"{offset=}", 2, offset + 13) def activate(self, flags): print("Activated by keyboard!") def click_within(self, evt): if evt.command == 0: return True if evt.command == 2: if \ evt.new_x >= 0 and evt.new_x < self.width and \ evt.new_y >= self.offset and evt.new_y < self.offset + self.height and \ evt.old_x >= 0 and evt.old_x < self.width and \ evt.old_y >= self.offset and evt.old_y < self.offset + self.height: return True else: print(f'{evt.old_x=} {evt.old_y=} {evt.new_x=} {evt.old_y=} {self.offset=} {self.width=} {self.height=}') async def async_thingy(self): print("Schedule async callback") await sleep(2) print("Finish async callback") def mouse_event(self, evt): self.x = evt.new_x self.y = evt.new_y if self.click_within(evt): Task(self.async_thingy()) print("Clicked!") return True class MyWindow(Window): def __init__(self): super().__init__(640, 480, title="Hello", doublebuffer=True) self.bgc = rgb(255,255,255) self.dejavu = Font("sans-serif", 13) self.mb = MenuBar((("File",'file'),("Help",'help'))) let _menu_File = MenuList() _menu_File.insert(MenuEntry("Test", lambda menu: print("hello, world"))) _menu_File.insert(MenuEntrySeparator()) _menu_File.insert(MenuEntry("Quit", lambda menu: self.close())) self.mb.insert('file', _menu_File) let _menu_Help = MenuList() let _menu_Help_help = MenuEntry("Help",lambda menu: print("oh no!")) _menu_Help.insert(_menu_Help_help) _menu_Help.insert(MyMenuWidget()) _menu_Help.insert(MyMenuWidget()) self.mb.insert('help', _menu_Help) self.mb.callback = lambda x: self.draw() def draw(self): self.fill(self.bgc) decor_render(self) let bounds = decor_get_bounds(self) self.mb.place(bounds['left_width'],bounds['top_height'],self.width-bounds['width'],self) self.mb.render(self) self.dejavu.draw_string(self,"Hello, world.",20,120) self.dejavu.draw_string(self,f"{self.x}, {self.y}",20,140) self.flip() def mouse_event(self, msg): let decResponse = decor_handle_event(msg) if decResponse == 2: print("Close me?") self.close() return True else if decResponse == 5: decor_show_default_menu(self, self.x + msg.new_x, self.y + msg.new_y) self.mb.mouse_event(self, msg) def interp(a,b,p): return a * (1.0 - p) + b * p def interp_color(a,b,p): return rgb(*(int(self.interp(l,r,p)) for l,r in zip(a,b))) async def animate(self): let mySentinel = object() self.animator = mySentinel let start = (255 * random.random(),255 * random.random(),255 * random.random()) let end = (255,255,255) for i in range(31): await sleep(0.033) if self.animator is not mySentinel: break self.bgc = self.interp_color(start,end,i/30) self.draw() def keyboard_event(self, msg): print(msg.keycode) if msg.keycode == 102 and msg.action == 1: Task(self.animate()) def close(self): super().close() mainloop.exit() def window_moved(self, msg): self.draw() def menu_close(self): self.draw() let w = MyWindow() w.move(200,200) w.draw() mainloop.menu_closed_callback = w.menu_close mainloop.run()