import pygame
-from bluetooth import BluetoothConf
-
tau = 2 * pi
MOVE_FOR_SEC = 1
EASE = staticmethod(ease_in_out_elastic((5 ** .5 - 1) / 2))
- def __init__(self, rect, update_callback, setting=False):
+ def __init__(self, parent, rect, update_callback, setting=False):
+ self.parent = parent
self.rect = rect
self.update_callback = update_callback
if setting is not None and not isinstance(setting, bool):
self.moving_since = nan
self.flip_again = False
- def handle_mousebuttondown(self, ev):
- if ev.button == 1 and self.rect.collidepoint(ev.pos):
- if self.moving_since is not nan:
- self.flip_again = True
- else:
- offset = self.MOVE_FOR_SEC / 2 if self.setting is None else 0
- self.setting = bool(self.setting) ^ True
- self.moving_since = time() - offset
-
- def update(self, new_value=None):
- if new_value is not None and new_value != self.setting:
- self.setting = new_value
- self.moving_since = time()
- return self.moving_since is not nan
-
- def draw(self, surf):
- pygame.draw.rect(surf, "gray", self.rect, 8)
+ def draw(self):
+ pygame.draw.rect(self.parent.surf, "gray", self.rect, 8)
t = time()
if t > self.moving_since + self.MOVE_FOR_SEC:
self.update_callback(self.setting)
args = (1 / 3, 2 * normalized_current, 0.5 + normalized_current * 0.5)
rgb = hsv_to_rgb(*args)
pygame.draw.circle(
- surf,
+ self.parent.surf,
pygame.Color(*(int(x * 255) for x in rgb)),
(base_left + eased_current * movement_width, self.rect.top + base_radius),
base_radius
)
+ def update(self, new_value=None):
+ if new_value is not None and new_value != self.setting:
+ self.setting = new_value
+ self.moving_since = time()
+ if self.moving_since is not nan:
+ self.parent.dirty = True
+
+ def handle_mousebuttondown(self, ev):
+ if ev.button == 1 and self.rect.collidepoint(ev.pos):
+ if self.moving_since is not nan:
+ self.flip_again = True
+ else:
+ offset = self.MOVE_FOR_SEC / 2 if self.setting is None else 0
+ self.setting = bool(self.setting) ^ True
+ self.moving_since = time() - offset
+
class Button:
- def __init__(self, rect, font, label, callback):
+ def __init__(self, parent, rect, label, callback):
+ self.parent = parent
self.rect = rect
- self.font = font
self.label = label
self.pushed = False
self.callback = callback
- def draw(self, surf):
+ def draw(self):
if not self.pushed:
- pygame.draw.rect(surf, "gray", self.rect, 8)
- fs = self.font.render(self.label, True, "gray")
+ frame_color = "gray"
+ fs = self.parent.font.render(self.label, True, "gray")
else:
- pygame.draw.rect(surf, "darkgray", self.rect)
- pygame.draw.rect(surf, "lightgray", self.rect, 4)
- fs = self.font.render(self.label, True, "black")
+ pygame.draw.rect(self.parent.surf, "darkgray", self.rect)
+ frame_color = "lightgray"
+ fs = self.parent.font.render(self.label, True, "black")
+ pygame.draw.rect(self.parent.surf, frame_color, self.rect, 8)
fs_size = fs.get_size()
center = self.rect.center
- surf.blit(fs, (center[0] - fs_size[0] // 2, center[1] - fs_size[1] // 2))
-
- def handle_event(self, ev):
- if ev.type == pygame.MOUSEBUTTONDOWN:
- if ev.button == 1 and self.rect.collidepoint(ev.pos):
- self.pushed = True
- return True
- elif ev.type == pygame.MOUSEBUTTONUP:
- if ev.button == 1 and self.pushed and self.rect.collidepoint(ev.pos):
- self.pushed = False
- self.callback()
- return True
- elif ev.type == pygame.MOUSEMOTION:
- if ev.buttons[0] and self.pushed and not self.rect.collidepoint(ev.pos):
- self.pushed = False
- return True
- return False
-
-
-def main():
- pygame.init()
- surf = pygame.display.set_mode((800, 600))
- clock = pygame.time.Clock()
- font = pygame.font.Font(None, size=64)
- bc = BluetoothConf()
- switch = Switch(pygame.Rect((64, 64), (256, 128)), bc.update, bc.conf)
-
- def cb():
- switch.update(not bc.conf)
- bc.update(not bc.conf)
-
- button = Button(pygame.Rect((64, 256), (384, 128)), font, "Apply", cb)
- running = True
- dirty = False
- while True:
- for ev in pygame.event.get():
- if ev.type == pygame.QUIT:
- running = False
- break
- elif ev.type == pygame.WINDOWEXPOSED:
- dirty = True
- elif ev.type == pygame.KEYDOWN:
- if ev.key == pygame.K_ESCAPE:
- running = False
- break
- elif ev.type == pygame.MOUSEBUTTONDOWN:
- switch.handle_mousebuttondown(ev)
- dirty |= button.handle_event(ev)
- if not running:
- break
- dirty |= switch.update()
- if dirty:
- surf.fill("black")
- switch.draw(surf)
- button.draw(surf)
- pygame.display.update()
- dirty = False
- clock.tick(60)
-
-
-if __name__ == "__main__":
- main()
+ self.parent.surf.blit(
+ fs, (center[0] - fs_size[0] // 2, center[1] - fs_size[1] // 2)
+ )
+
+ def update(self):
+ pass
+
+ def handle_mousebuttondown(self, ev):
+ if ev.button == 1 and self.rect.collidepoint(ev.pos):
+ self.pushed = True
+ self.parent.dirty = True
+
+ def handle_mousebuttonup(self, ev):
+ if ev.button == 1 and self.pushed and self.rect.collidepoint(ev.pos):
+ self.pushed = False
+ self.callback()
+ self.parent.dirty = True
+
+ def handle_mousemotion(self, ev):
+ if not self.pushed:
+ return
+ if ev.buttons[0] and not self.rect.collidepoint(ev.pos):
+ self.pushed = False
+ self.parent.dirty = True
+
+
+class IconButton:
+ def __init__(self, parent, name, shape, shape_size, rect):
+ self.parent = parent
+ self.name = name
+ self.shape = shape
+ self.shape_size = shape_size
+ self.rect = rect
+
+ def draw(self):
+ color = "lime " if self.parent.xrandr_conf.is_active(self.name) else "gray"
+ pygame.draw.rect(self.parent.surf, color, self.rect, 8)
+ units = (16, 16)
+ full_size = (units[0] * self.shape_size[0], units[1] * self.shape_size[1])
+ center = self.rect.center
+ self.shape.draw(
+ self.parent.surf,
+ (center[0] - full_size[0] // 2, center[1] - full_size[1] // 2),
+ units,
+ "white",
+ )
+
+ def update(self):
+ pass
+
+ def handle_mousebuttondown(self, ev):
+ if not self.rect.collidepoint(ev.pos):
+ return
+ self.parent.xrandr_conf.update(self.name)
+ if self.name != "single":
+ self.parent.bluetooth_conf.update(True)
+ self.parent.bt_switch.update(True)
+ settings_per_device_type = {
+ "touchpad": self.parent.touch_switch.setting,
+ "stylus": self.parent.stylus_switch.setting,
+ }
+ for device_type, setting in settings_per_device_type.items():
+ if setting is not None:
+ self.parent.xinput_conf.update_by_type(device_type, setting)
+ self.parent.dirty = True
+
+
+class Icon:
+ def __init__(self, parent, shape, pos):
+ self.parent = parent
+ self.shape = shape
+ self.pos = pos
+
+ def update(self):
+ pass
+
+ def draw(self):
+ self.shape.draw(self.parent.surf, self.pos, (8, 8), "gray")
StrokeLine((11, 11), (12, 12), 1),
]
)
+bluetooth = Shapes(
+ [
+ StrokeLine((10, 2), (15, 6), 1.5),
+ StrokeLine((15, 6), (5, 14), 1.5),
+ StrokeLine((10, 2), (10, 19), 1.5),
+ StrokeLine((10, 19), (15, 15), 1.5),
+ StrokeLine((15, 15), (5, 7), 1.5),
+ ]
+)
shapes = [
{
"shape": laptop_single,
"shape": stylus,
"pos": (600, 600),
},
+ {
+ "shape": bluetooth,
+ "pos": (1100, 600),
+ },
]
import pygame
from bluetooth import BluetoothConf
-from ui import Button, Switch
-from vectors import laptop_double, laptop_single, laptop_vertical, touchscreen, stylus
+from ui import Button, Icon, IconButton, Switch
+from vectors import (
+ bluetooth,
+ laptop_double,
+ laptop_single,
+ laptop_vertical,
+ touchscreen,
+ stylus,
+)
from xinput import XinputConf
from xrandr import XrandrConf
class ZenbookConf:
- SHAPES = (
- {
- "name": "single",
- "shape": laptop_single,
- "rect": pygame.Rect((68, 68), (416, 416)),
- },
- {
- "name": "double",
- "shape": laptop_double,
- "rect": pygame.Rect((568, 68), (416, 416)),
- },
- {
- "name": "vertical",
- "shape": laptop_vertical,
- "rect": pygame.Rect((1068, 68), (416, 416)),
- },
- )
-
def __init__(self):
pygame.init()
self.surf = pygame.display.set_mode((1536, 1200))
self.xinput_conf = XinputConf(self.xrandr_conf)
self.bluetooth_conf = BluetoothConf()
self.current_fps = -1
- self.bt_switch = Switch(
+ bt_switch = Switch(
+ self,
pygame.Rect((128, 552), (256, 128)),
self.bluetooth_conf.update,
self.bluetooth_conf.conf
)
- self.touch_switch = Switch(
+ touch_switch = Switch(
+ self,
pygame.Rect((128, 748), (256, 128)),
partial(self.xinput_conf.update_by_type, "touchpad"),
self.xinput_conf.conf_by_type("touchpad"),
)
- self.touch_button = Button(
- pygame.Rect((784, 748), (384, 128)),
- self.font,
- "Re-apply",
- partial(self.xinput_conf.reapply_by_type, "touchpad")
- )
- self.stylus_switch = Switch(
+ stylus_switch = Switch(
+ self,
pygame.Rect((128, 944), (256, 128)),
partial(self.xinput_conf.update_by_type, "stylus"),
self.xinput_conf.conf_by_type("stylus"),
)
- self.stylus_button = Button(
- pygame.Rect((784, 944), (384, 128)),
- self.font,
- "Re-apply",
- partial(self.xinput_conf.reapply_by_type, "stylus")
+ self.children = (
+ IconButton(
+ self,
+ "single",
+ laptop_single,
+ (22, 22),
+ pygame.Rect((68, 68), (416, 416)),
+ ),
+ IconButton(
+ self,
+ "double",
+ laptop_double,
+ (22, 22),
+ pygame.Rect((568, 68), (416, 416)),
+ ),
+ IconButton(
+ self,
+ "vertical",
+ laptop_vertical,
+ (22, 22),
+ pygame.Rect((1068, 68), (416, 416)),
+ ),
+ bt_switch,
+ Icon(
+ self,
+ bluetooth,
+ (bt_switch.rect.right + 68, bt_switch.rect.centery - 88),
+ ),
+ touch_switch,
+ Icon(
+ self,
+ touchscreen,
+ (touch_switch.rect.right + 68, touch_switch.rect.centery - 88),
+ ),
+ stylus_switch,
+ Icon(
+ self,
+ stylus,
+ (stylus_switch.rect.right + 68, stylus_switch.rect.centery - 88),
+ ),
+ Button(
+ self,
+ pygame.Rect((784, 748), (384, 128)),
+ "Re-apply",
+ partial(self.xinput_conf.reapply_by_type, "touchpad")
+ ),
+ Button(
+ self,
+ pygame.Rect((784, 944), (384, 128)),
+ "Re-apply",
+ partial(self.xinput_conf.reapply_by_type, "stylus")
+ ),
)
def handle_event(self, ev):
if ev.key == pygame.K_ESCAPE:
self.running = False
return False
- elif ev.type == pygame.MOUSEBUTTONDOWN:
- for item in self.SHAPES:
- if item["rect"].collidepoint(ev.pos):
- self.xrandr_conf.update(item["name"])
- if item["name"] != "single":
- self.bluetooth_conf.update(True)
- self.bt_switch.update(True)
- settings_per_device_type = {
- "touchpad": self.touch_switch.setting,
- "stylus": self.stylus_switch.setting,
- }
- for device_type, setting in settings_per_device_type.items():
- if setting is not None:
- self.xinput_conf.update_by_type(device_type, setting)
- self.dirty = True
- else:
- self.bt_switch.handle_mousebuttondown(ev)
- self.touch_switch.handle_mousebuttondown(ev)
- self.stylus_switch.handle_mousebuttondown(ev)
- self.dirty |= self.touch_button.handle_event(ev)
- self.dirty |= self.stylus_button.handle_event(ev)
+ method_name = f"handle_{pygame.event.event_name(ev.type).lower()}"
+ for child in self.children:
+ method = getattr(child, method_name, None)
+ if method is None:
+ continue
+ method(ev)
return True
def update(self):
- self.dirty |= self.bt_switch.update()
- self.dirty |= self.touch_switch.update()
- self.dirty |= self.stylus_switch.update()
+ for child in self.children:
+ child.update()
if self.clock.get_fps() != self.current_fps:
self.current_fps = int(self.clock.get_fps())
self.dirty = True
def draw(self):
self.surf.fill(0x333333)
- for item in self.SHAPES:
- if self.xrandr_conf.is_active(item["name"]):
- pygame.draw.rect(self.surf, "lime", item["rect"], 8)
- else:
- pygame.draw.rect(self.surf, "gray", item["rect"], 8)
- item["shape"].draw(
- self.surf,
- (item["rect"].left + 32, item["rect"].top + 32),
- (16, 16),
- "white",
- )
-
- self.bt_switch.draw(self.surf)
- fs = self.font.render("Bluetooth", True, "gray")
- r = self.bt_switch.rect
- self.surf.blit(fs, (r.right + 68, r.centery - fs.get_height() // 2))
-
- self.touch_switch.draw(self.surf)
- touchscreen.draw(
- self.surf,
- (self.touch_switch.rect.right + 68, self.touch_switch.rect.centery - 88),
- (8, 8),
- "gray",
- )
- self.touch_button.draw(self.surf)
-
- self.stylus_switch.draw(self.surf)
- stylus.draw(
- self.surf,
- (self.stylus_switch.rect.right + 68, self.stylus_switch.rect.centery - 88),
- (8, 8),
- "gray",
- )
- self.stylus_button.draw(self.surf)
+ for child in self.children:
+ child.draw()
def run(self):
while True: