diff --git a/jgui/logger/logger.py b/jgui/logger/logger.py index f9b2c5b..c414a7f 100644 --- a/jgui/logger/logger.py +++ b/jgui/logger/logger.py @@ -1,6 +1,6 @@ from __future__ import print_function -from jgui.settings import DEBUG +import jgui.settings as settings def log(*args): - if DEBUG: + if settings.DEBUG: print(*args) diff --git a/jgui/surface/surface.py b/jgui/surface/surface.py index 3b24772..050d572 100644 --- a/jgui/surface/surface.py +++ b/jgui/surface/surface.py @@ -6,22 +6,41 @@ import math from .structures import Size, Position, Rectangle, Color, BorderRadius, Padding, Gradient, RadialGradient from ..events.events import WindowEventSource from ..logger import log +from datetime import datetime +from jgui.settings import DEBUG as debug -debug = True -class Surface(object): - def __init__(self, size=None, context=None, render_mouse=True): +class Surface(WindowEventSource): + def __init__(self, size=None, context=None, data=None, render_mouse=True, show_fps=False): + super(Surface, self).__init__() + self.show_fps = show_fps self.size = Size.from_value(size) + self.mouse_pos = Position() if context is None: - self.csurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.size.width, self.size.height) + if data is not None: + self.csurface = cairo.ImageSurface.create_for_data(data, cairo.FORMAT_ARGB32, self.size.width, self.size.height) + else: + self.csurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.size.width, self.size.height) self.context = cairo.Context(self.csurface) else: self.context = context - self.root_window = Window('root', position=Position(0,0), size=self.size, context=self.context) + self.root_window = Window('root', position=Position(0,0), size=self.size, context=self.context, surface=self) self.render_mouse = render_mouse if self.render_mouse: - self.mouse_icon = Mouse('mouse', position=Position(0, 0), size=Size(12,20), context=self.context) - self.windows = [self.root_window, self.mouse_icon] + self.mouse_icon = Mouse('mouse', position=Position(0, 0), size=Size(12,20), context=self.context, surface=self) + self.fps_counter = TextWindow('fps', '0 fps', position=Position(self.size.width-70, 10), size=Size(80,20), context=self.context, surface=self) + self.windows = [self.root_window, self.mouse_icon, self.fps_counter] + + self.current_hover_window = self.root_window + self.current_focused_window = self.root_window + + self.dt = 0 + self.old_time = datetime.now() + self.dtindex = 0 + self.max_samples = 60 + self.dtlist = range(self.max_samples) + + self.accept('mouse-move', self.process_mouse_move) def setTopZero(self, context): context.identity_matrix() @@ -29,17 +48,57 @@ class Surface(object): 1, 0, 0) context.transform(matrix) - def inject_mouse_position(self, pos): - if self.render_mouse: - self.mouse_icon.position = pos - self.root_window.inject_mouse_position(pos) - def inject_mouse_down(self, button): + self.current_focused_window = self.current_hover_window self.root_window.inject_mouse_down(button) def inject_mouse_up(self, button): self.root_window.inject_mouse_up(button) + def inject_mouse_position(self, pos): + mouse_pos = Position.from_value(pos) + diff = mouse_pos - self.mouse_pos + old_pos = self.mouse_pos + self.mouse_pos = mouse_pos + if diff != Position(0,0): + self.dispatch('mouse-move', self, old_pos, self.mouse_pos) + + if self.render_mouse: + self.mouse_icon.position = pos + self.root_window.inject_mouse_position(pos) + + def mouse_inside(self): + """ + Checks if the mouse is inside the window taking into account + all other windows and draw priorities. + """ + + #Check all children of root to see if there is a higher priority + #window than the current one + root = self.root_window + stack = [root] + visited = set() + while stack: + item = stack[-1] + rec = item.rectangle + clip_parent = item.get_clip_parent() + if clip_parent is not None: + rec = item.rectangle.intersection(clip_parent.rectangle) + intersects_mouse = rec.intersects_with(self.mouse_pos) + if item.children and not set(item.children).issubset(visited): + stack.extend(item.children) + else: + if intersects_mouse: + return item + visited.add(item) + stack.pop() + + return None + + def process_mouse_move(self, obj, old_mpos, new_mpos): + if not self.current_hover_window or not self.current_hover_window.mouse_down: + self.current_hover_window = self.mouse_inside() + def notify_window_resize(self, width, height): self.size = Size(width, height) self.root_window.size = self.size @@ -53,9 +112,21 @@ class Surface(object): if item.children: stack.extend(item.children) + def calcfps(self, dt): + self.dtlist[self.dtindex] = float(dt) + self.dtindex += 1 + if self.dtindex >= self.max_samples: + self.dtindex = 0 + + return 1.0/(sum(self.dtlist)/float(self.max_samples)) + def draw(self): - #self.setTopZero(ctx) - #self.context.identity_matrix() + if self.show_fps: + self.dt = (datetime.now() - self.old_time).total_seconds() + self.old_time = datetime.now() + fps = self.calcfps(self.dt) + self.fps_counter.text = '{} fps'.format(round(fps)) + self.context.set_operator(cairo.OPERATOR_CLEAR) self.context.rectangle(0.0, 0.0, self.size.width, self.size.height) self.context.fill() @@ -64,7 +135,6 @@ class Surface(object): window.draw() - class WindowSurface(object): line_joins = {'miter': cairo.LINE_JOIN_MITER, @@ -118,7 +188,7 @@ class WindowSurface(object): def draw_circle(self, position, size, color=(1,1,1,1), line_width=1.0, line_color=(0,0,0,1), start_angle=0.0, end_angle=360.0): color = Color.from_value(color) line_color = Color.from_value(line_color) - context = self.context + context = self.surface.context position = Position.from_value(position) size = Size.from_value(size) width = size.width @@ -148,7 +218,7 @@ class WindowSurface(object): center_horizontal=True, center_vertical=True, image_offset=(0, 0)): - context = self.context + context = self.surface.context position = Position.from_value(position) offset = Position.from_value(image_offset) size = Size.from_value(size) @@ -158,9 +228,6 @@ class WindowSurface(object): x,y = (self.position.x+position.x, self.position.y+position.y) - context.rectangle(x, y, width, height) - context.clip() - if isinstance(image, basestring): image = self.load_image(image) @@ -229,7 +296,7 @@ class WindowSurface(object): color = Color.from_value(font_color) background_color = Color.from_value(background_color) position = Position.from_value(position) - context = self.context + context = self.surface.context font_weight = self.font_weights[font_weight] font_style = self.font_styles[font_style] @@ -264,7 +331,7 @@ class WindowSurface(object): if lines: line_color = Color.from_value(line_color) background_color = Color.from_value(background_color) - context = self.context + context = self.surface.context start_pos = self.position + lines[0] context.set_line_width(line_width) context.move_to(start_pos.x, start_pos.y) @@ -286,7 +353,7 @@ class WindowSurface(object): context.stroke() def render_radial_gradient(self, gradient, inner_radius=None, outer_radius=None): - context = self.context + context = self.surface.context position = self.position size = self.size gradient = RadialGradient.from_value(gradient) @@ -306,7 +373,7 @@ class WindowSurface(object): context.restore() def render_linear_gradient(self, gradient): - context = self.context + context = self.surface.context position = self.position size = self.size gradient = Gradient.from_value(gradient) @@ -331,7 +398,7 @@ class WindowSurface(object): corner_radius = BorderRadius.from_value(corner_radius) gradient = Gradient.from_value(gradient) - context = self.context + context = self.surface.context radius = corner_radius degrees = math.pi / 180.0 x = position.x + self.position.x @@ -341,19 +408,7 @@ class WindowSurface(object): if clip: #clips the entire region so any child windows will be confined to the parent context.new_path() - context.arc(x + width - radius.topright - line_width/2.0, - y + radius.topright + line_width/2.0, - radius.topright+self.border_width/2.0, -90 * degrees, 0 * degrees) - context.arc(x + width - radius.bottomright - line_width/2.0, - y + height - radius.bottomright - line_width/2.0, - radius.bottomright+self.border_width/2.0, 0 * degrees, 90 * degrees) - context.arc(x + radius.bottomleft + line_width/2.0, - y + height - radius.bottomleft - line_width/2.0, - radius.bottomleft+self.border_width/2.0, 90 * degrees, 180 * degrees) - context.arc(x + radius.topleft + line_width/2.0, - y + radius.topleft + line_width/2.0, - radius.topleft+self.border_width/2.0, 180 * degrees, 270 * degrees) - context.close_path() + context.rectangle(x, y, width, height) context.clip() context.new_path() @@ -414,11 +469,11 @@ class WindowSurface(object): def draw(self): if self.visible: - self.context.save() + self.surface.context.save() self.render() for child in self.children: child.draw() - self.context.restore() + self.surface.context.restore() class Window(WindowEventSource, WindowSurface): @@ -432,7 +487,7 @@ class Window(WindowEventSource, WindowSurface): position = Position.from_value(kwargs.pop('position', Position())) size = Size.from_value(kwargs.pop('size', Size())) - self._context = kwargs.pop('context', None) + self._surface = kwargs.pop('surface', None) self.min_size = Size.from_value(kwargs.pop('min_size', Size(1,1))) self.max_size = Size.from_value(kwargs.pop('max_size', Size(-1,-1))) self.corner_handle_size = Size.from_value(kwargs.pop('corner_handle_size', Size(20, 20))) @@ -472,7 +527,7 @@ class Window(WindowEventSource, WindowSurface): self.accept('mouse-move', self.process_mouse_move) self.size = size self.position = position - self.context = self._context + self.surface = self._surface self.resizable = kwargs.pop('resizable', self._resizable) self.draggable = kwargs.pop('draggable', self._draggable) @@ -903,56 +958,10 @@ class Window(WindowEventSource, WindowSurface): return self._root def mouse_inside(self): - """ - Checks if the mouse is inside the window taking into account - all other windows and draw priorities. - """ - rec = self.rectangle - clip_parent = self.get_clip_parent() - if clip_parent is not None: - rec = self.rectangle.intersection(clip_parent.rectangle) - res = rec.intersects_with(self.mouse_pos) - if not res: - return False - - #Check all children of root to see if there is a higher priority - #window than the current one - root = self.root - stack = [root] - visited = set() - while stack: - item = stack[-1] - rec = item.rectangle - clip_parent = item.get_clip_parent() - if clip_parent is not None: - rec = item.rectangle.intersection(clip_parent.rectangle) - intersects_mouse = rec.intersects_with(self.mouse_pos) - if item.children and not set(item.children).issubset(visited): - stack.extend(item.children) - else: - if item is self: - break - else: - res &= not intersects_mouse - visited.add(item) - stack.pop() - - return res + return self is self.surface.current_hover_window def mouse_held(self): - res = self.mouse_down - stack = [self.root] - visited = set() - while len(stack) > 0: - item = stack[-1] - if item.children and not set(item.children).issubset(visited): - stack.extend(item.children) - else: - if self is not item: - res |= item.mouse_down - visited.add(item) - stack.pop() - return res + return self.mouse_down def process_mouse_move(self, obj, old_mpos, new_mpos): mouse_focus = self.mouse_inside() @@ -970,14 +979,14 @@ class Window(WindowEventSource, WindowSurface): self.mouse_in = False @property - def context(self): - return self._context + def surface(self): + return self._surface - @context.setter - def context(self, context): - self._context = context + @surface.setter + def surface(self, surface): + self._surface = surface for child in self.children: - child.context = self._context + child.surface = self._surface def add_child(self, child_window): if child_window not in self.children: @@ -985,7 +994,7 @@ class Window(WindowEventSource, WindowSurface): child_window.position = child_window.position + self.position +\ [self.border_width/2, self.border_width/2] +\ [self.padding.left, self.padding.top] - child_window.context = self.context + child_window.surface = self.surface self.children.append(child_window) def remove_child(self, child_window): diff --git a/test.py b/test.py index 72333e8..d96c8e4 100644 --- a/test.py +++ b/test.py @@ -11,22 +11,24 @@ from pandac.PandaModules import * from testclasses import * class Test(ShowBase): - def __init__(self, width=1024, height=1024): + def __init__(self, width=800, height=600): ShowBase.__init__(self) self._render = True self.width = width self.height = height props = WindowProperties() + props.setCursorHidden(True) props.setSize(width, height) base.setBackgroundColor(0.5,1,1,1) - base.cam.setPos(0,-3.7,0) + base.cam.setPos(0,-10,0) base.win.requestProperties(props) - base.disableMouse() c = CardMaker('plane') c.setFrame(-1, 1, 1, -1) c.setHasUvs(True) screen = render2d.attachNewNode(c.generate()) screen.setTransparency(TransparencyAttrib.MAlpha) + screen.setPos(0,0,0) + base.buttonThrowers[0].node().setMoveEvent('mouse_move') self.surf = TestSurface([width, height]) @@ -76,7 +78,8 @@ class Test(ShowBase): def drawall(self, task): if self._render: self.surf.draw() - self.cairoTexture.setRamImage(self.surf.csurface.get_data()) + ri = self.cairoTexture.modifyRamImage() + ri.setData(self.surf.csurface.get_data()) return task.cont def mousemove(self, task): diff --git a/testclasses.py b/testclasses.py index 4764fc2..1094181 100644 --- a/testclasses.py +++ b/testclasses.py @@ -5,6 +5,7 @@ from jgui.settings import IMG_DIR class TestSurface(Surface): def __init__(self, *args, **kwargs): super(TestSurface, self).__init__(*args, **kwargs) + self.show_fps = True my_win = Window('child', position=[0,0], size=[500,200], @@ -16,8 +17,8 @@ class TestSurface(Surface): border_radius=[40,20], border_color=(0,0,0), background_color=(1,1,1), - clip_children=True, ignore_debug=True, + clip_children=True, background_image=os.path.join(IMG_DIR, 'wrench.png'), background_image_filter='bilinear', background_image_keep_ratio=True, @@ -52,7 +53,7 @@ class TestSurface(Surface): background_image_keep_ratio=True, background_image_center_vertical=False, background_image_center_horizontal=False, - gradient=Gradient(stops=[(0, Color(1,1,1)), (0.8, Color(0.5,0.5,0.5)), (1, Color(0.7,0.7,0.7))])) + gradient=Gradient(stops=[(0, Color(1,1,1)), (0.8, Color(0.5,0.5,0.5, 0.5)), (1, Color(0.7,0.7,0.7,0.5))])) my_win2.add_child(TextWindow('text2',"I haven't had a bean in forever.", position=[20,100], size=[200, 100],