diff --git a/jgui/__init__.py b/jgui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jgui/events/__init__.py b/jgui/events/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jgui/events/events.py b/jgui/events/events.py new file mode 100644 index 0000000..42bad4c --- /dev/null +++ b/jgui/events/events.py @@ -0,0 +1,37 @@ + +class Event(object): + def __init__(self, name='event', data=None): + self.name = name + self.data = data + +class EventSource(object): + types = [] # list of event names + + def __init__(self): + super(EventSource, self).__init__() + self.events = {} + for type in self.types: + self.events[type] = [] + + def accept(self, event, callback): + if event in self.events: + if callback not in self.events[event]: + self.events[event].append(callback) + + def dispatch(self, event, *args, **kwargs): + for callback in self.events[event]: + callback(*args, **kwargs) + + def reject(self, event, callback): + if event in self.events: + if callback in self.events[event]: + self.events[event].remove(callback) + +class WindowEventSource(EventSource): + types = ['mouse-enter', 'mouse-leave', + 'resize', 'move', + 'key-repeat', 'key-down', 'key-up', + 'mouse-right-down', 'mouse-right-up', + 'mouse-left-down', 'mouse-left-up', + 'mouse-middle-down', 'mouse-middle-up', + 'scroll-up', 'scroll-down', 'drag'] diff --git a/jgui/surface/__init__.py b/jgui/surface/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jgui/surface/structures.py b/jgui/surface/structures.py new file mode 100644 index 0000000..1636dbc --- /dev/null +++ b/jgui/surface/structures.py @@ -0,0 +1,126 @@ + +class StructureBase(object): + @classmethod + def _attrs(cls): + a = [] + i = cls() + for attr in dir(i): + if not attr.startswith('_') and not callable(getattr(i, attr)): + a.append(attr) + return a + + def _dict(self): + d = {} + attrs = self._attrs() + for a in attrs: + d[a] = getattr(self, a) + return d + + def _dict_string(self): + return ', '.join('{}={}'.format(key, val) for key, val in self._dict().items()) + + def __repr__(self): + return unicode(self) + + def __str__(self): + return unicode(self).encode('utf-8') + + def __unicode__(self): + return u'{} [{}]'.format(self.__class__.__name__, self._dict_string()) + + @classmethod + def is_like(cls, obj): + """Checks if the object is like the current class + If it has everything a duck has, then it's good.""" + for key in cls._attrs(): + if not hasattr(obj, key): + return False + return True + +class Position(StructureBase): + def __init__(self, x=0, y=0): + self.x = x + self.y = y + + def __add__(self, other): + other = Position.from_value(other) + return Position(self.x+other.x, self.y+other.y) + + def __sub__(self, other): + other = Position.from_value(other) + return Position(self.x-other.x, self.y-other.y) + + @classmethod + def from_value(cls, value): + if not value: + return cls() + if cls.is_like(value): + return value + elif len(value) >= 2: + return cls(value[0], value[1]) + else: + return cls() + + +class Size(StructureBase): + def __init__(self, width=0, height=0): + self.height = height + self.width = width + + def __add__(self, other): + other = Size.from_value(other) + return Size(self.width+other.width, self.height+other.height) + + def __sub__(self, other): + other = Size.from_value(other) + return Size(self.width-other.width, self.height-other.height) + + @classmethod + def from_value(cls, value): + if not value: + return cls() + if cls.is_like(value): + return value + elif len(value) >= 2: + return cls(value[0], value[1]) + else: + return cls() + + +class Rectangle(StructureBase): + def __init__(self, position=None, size=None): + """ A rectangle object with coordinates and size. + position: can take the first two values of an array or a Position object + size: can take the first two values of an array or a Size object + """ + self._position = None + self._size = None + self.position = position + self.size = size + + @property + def position(self): + return self._position + + @position.setter + def position(self, position): + self._position = Position.from_value(position) + + @property + def size(self): + return self._size + + @size.setter + def size(self, size): + self._size = Size.from_value(size) + + def intersects_with(self, position): + """Checks if the position is within the bounds of the rectangle including the edges""" + pos = Position.from_value(position) + if pos.x < self.position.x + self.size.width and\ + pos.x >= self.position.x: + if pos.y < self.position.y + self.size.height and\ + pos.y >= self.position.y: + return True + return False + diff --git a/jgui/surface/surface.py b/jgui/surface/surface.py new file mode 100644 index 0000000..6a0e888 --- /dev/null +++ b/jgui/surface/surface.py @@ -0,0 +1,91 @@ +import cairo +import math +from .structures import Size, Position, Rectangle +from ..events.events import WindowEventSource + +class Surface(object): + def __init__(self, size=None): + self.size = Size.from_value(size) + self.csurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.size.width, self.size.height) + self.root_window = Window('root', Position(0,0), self.size) + self.windows = [self.root_window] + + def draw(self): + for window in self.windows: + window.draw(self.csurface) + +surface = None +def init(size): + surface = Surface(size) + +class WindowSurface(object): + def __init__(self): + super(WindowSurface, self).__init__() + #self.mouse_pos + + def draw(self, surface): + Width = self.size.width + Height = self.size.height + x,y, radius = (Width/2,Height/2, Width/2-Width/10) + ctx = cairo.Context(surface) + ctx.set_operator(cairo.OPERATOR_CLEAR) + ctx.rectangle(0.0, 0.0, Width, Height) + ctx.fill() + + ctx.set_operator(cairo.OPERATOR_OVER) + ctx.set_line_width(Width/10) + ctx.arc(x, y, radius, 0, 2.0 * math.pi) + ctx.set_source_rgba(0.8, 0.8, 0.8) + ctx.fill_preserve() + ctx.set_source_rgba(1, 1, 1) + ctx.stroke() + + +class Window(WindowEventSource, WindowSurface): + def __init__(self, name, position=None, size=None): + super(Window, self).__init__() + self.name = name + self.rectangle = Rectangle(position, size) + self.children = [] + self.parent = None + self.mouse_pos = Position() + + + def add_child(self, child_window): + if child_window not in self.children: + child_window.parent = self + self.children.append(child_window) + + def remove_child(self, child_window): + try: + self.children.remove(child_window) + child_window.parent = None + except ValueError: + pass + + @property + def position(self): + return self.rectangle.position + + @position.setter + def position(self, position): + diff = position - self.rectangle.position + if diff.x != 0 or diff.y != 0: + self.dispatch('move', self, position) + for child in self.children: + child.position = child.position + diff + self.recangle.position = position + + @property + def size(self): + return self.rectangle.size + + @size.setter + def size(self, size): + diff = size - self.rectangle.size + if diff.height != 0 or diff.width != 0: + self.dispatch('resize', self, size) + for child in self.children: + child.size = child.size + diff + self.rectangle.size = size + diff --git a/test.py b/test.py new file mode 100644 index 0000000..504bda4 --- /dev/null +++ b/test.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +import cairo +import pygame +import array +import math +import sys + +import direct.directbase.DirectStart +from pandac.PandaModules import * + +from jgui.surface.surface import * + +props = WindowProperties() +props.setSize(1024,1024) +base.setBackgroundColor(0.5,1,1,1) +base.cam.setPos(0,-10,0) +base.win.requestProperties(props) +screen = loader.loadModel('/home/joey/Creationista/models/plane.egg.pz') +screen.setTransparency(TransparencyAttrib.MAlpha) +screen.setTwoSided(True) +screen.setScale(2) +Width, Height = 1024, 1024 + +surf = Surface([Width, Height]) + +cairoTexture = Texture() +cairoTexture.setFormat(cairoTexture.FRgba8) +cairoTexture.setup2dTexture(Width, Height, Texture.CMDefault, Texture.FRgba32) + +screen.setTexture(cairoTexture) +screen.reparentTo(render2d) + +def drawall(task): + surf.draw() + cairoTexture.setRamImage(surf.csurface.get_data()) + return task.cont + +taskMgr.add(drawall, 'draw') +run()