Example #1
0
    def test_conversion(self):

        a = Pos(10, 15)
        b = Pos(12, 21)

        tmp = a.grid_cr()
        self.assertEquals(tmp.x, 1)
        self.assertEquals(tmp.y, 0)

        tmp = a.view_xy()
        self.assertEquals(tmp.x, 100)
        self.assertEquals(tmp.y, 240)

        a.snap_to_grid()
        self.assertEquals(a.x, 10)
        self.assertEquals(a.y, 0)

        b = Pos(10, 16)

        tmp = b.grid_cr()
        self.assertEquals(tmp.x, 1)
        self.assertEquals(tmp.y, 1)

        tmp = b.view_xy()
        self.assertEquals(tmp.x, 100)
        self.assertEquals(tmp.y, 256)

        b.snap_to_grid()
        self.assertEquals(b.x, 10)
        self.assertEquals(b.y, 16)

        c = Pos(11, 17)

        tmp = c.grid_cr()
        self.assertEquals(tmp.x, 1)
        self.assertEquals(tmp.y, 1)

        tmp = c.view_xy()
        self.assertEquals(tmp.x, 110)
        self.assertEquals(tmp.y, 272)

        c.snap_to_grid()
        self.assertEquals(c.x, 10)
        self.assertEquals(c.y, 16)
Example #2
0
 def calc_position(self, x, y):
     """Calculate the grid view position."""
     pos = Pos(x, y)
     pos.snap_to_grid()
     return pos
Example #3
0
class MatrixView(Gtk.DrawingArea):

    def __init__(self, lmd):
        super(MatrixView, self).__init__()
        self._surface = None
        self._hover_pos = Pos(0, 0)
        self.set_can_focus(True)
        self.set_focus_on_click(True)
        self.connect('draw', self.on_draw)
        self.connect('configure-event', self.on_configure)
        self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
        self.connect('button-press-event', self.on_button_press)
        # https://stackoverflow.com/questions/44098084/how-do-i-handle-keyboard-events-in-gtk3
        self.add_events(Gdk.EventMask.KEY_PRESS_MASK)
        self.connect('key-press-event', self.on_key_press)
        self.add_events(Gdk.EventMask.POINTER_MOTION_MASK)
        self.connect('motion-notify-event', self.on_hover)
        self._cursor_on = True
        self._hover_pos = Pos(0, 0)
        self.init_line_matching_data(lmd)
        # https://developer.gnome.org/gtk3/stable/GtkWidget.html#gtk-widget-add-tick-callback
        self.start_time = time.time()
        self.cursor_callback = self.add_tick_callback(self.toggle_cursor)
        pub.subscribe(self.on_matching_data_changed, 'MATCHING_DATA_CHANGED')

    def init_surface(self, area):
        """Initialize Cairo surface."""
        if self._surface is not None:
            # destroy previous buffer
            self._surface.finish()
            self._surface = None
        # create a new buffer
        self._surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, area.get_allocated_width(), area.get_allocated_height())

    def init_line_matching_data(self, lmd):
        self.on_matching_data_changed(lmd)

    def calc_offset(self):
        """Calculate the upper left coordinate where the matrix will be drawn."""
        grid_w = Preferences.values['GRIDSIZE_W']
        grid_h = Preferences.values['GRIDSIZE_H']
        x_offset = round((self._surface.get_width() - 3 * grid_w) / 2)
        y_offset = round((self._surface.get_height() - 3 * grid_h) / 2)
        self._offset = Pos(x_offset, y_offset)
        self._offset.snap_to_grid()

    def on_configure(self, area, event, data=None):
        self.init_surface(self)
        self.calc_offset()
        context = cairo.Context(self._surface)
        self.do_drawing(context)
        self._surface.flush()
        return False

    def on_matching_data_changed(self, lmd):
        self._matrix = lmd.pattern
        self._start_char = lmd.char
        self._start_ori = lmd.ori

    def on_button_press(self, button, event):
        return True

    def on_key_press(self, widget, event):

        # TODO Will this work in other locale too?
        def filter_non_printable(ascii):
            char = ''
            if (ascii > 31 and ascii < 255) or ascii == 9:
                char = chr(ascii)
            return char

        def valid_index(pos):
            if pos.x >= 0 and pos.x < 3 and pos.y >= 0 and pos.y < 3:
                return True
            else:
                return False

        def next_char():
            # move to the next character or the next line
            if grid_pos.x < 2:
                self._hover_pos += Pos(1, 0).view_xy()
            elif grid_pos.y < 2:
                self._hover_pos += Pos(-2, 1).view_xy()

        def previous_char():
            # move to the previous character or the previous line
            if grid_pos.x > 0:
                if grid_pos.x <= 2:
                    self._hover_pos -= Pos(1, 0).view_xy()
            elif grid_pos.y > 0:
                self._hover_pos += Pos(2, -1).view_xy()

        grid_pos = self._hover_pos - self._offset
        grid_pos.snap_to_grid()
        grid_pos = grid_pos.grid_cr()
        value = event.keyval
        if value in (Gdk.KEY_Shift_L, Gdk.KEY_Shift_R):
            pass
        elif value == Gdk.KEY_Left or value == Gdk.KEY_BackSpace:
            previous_char()
        elif value == Gdk.KEY_Right:
            next_char()
        elif value == Gdk.KEY_Up:
            self._hover_pos -= Pos(0, 1).view_xy()
        elif value == Gdk.KEY_Down:
            self._hover_pos += Pos(0, 1).view_xy()
        elif value & 255 != 13:  # enter
            if valid_index(grid_pos):
                str = filter_non_printable(value)
                self._matrix[grid_pos.y][grid_pos.x] = str
                next_char()
        return True

    def on_hover(self, widget, event):
        if not self.has_focus():
            self.grab_focus()
        self._hover_pos = Pos(event.x, event.y)
        self._hover_pos.snap_to_grid()
        self.queue_resize()

    def on_draw(self, area, ctx):
        if self._surface is not None:
            ctx.set_source_surface(self._surface, 0.0, 0.0)
            ctx.paint()
        else:
            print(_("Invalid surface"))
        return False

    def do_drawing(self, ctx):
        self.draw_gridlines(ctx)
        self.draw_content(ctx)
        self.draw_cursor(ctx)

    def draw_gridlines(self, ctx):
        grid_w = Preferences.values['GRIDSIZE_W']
        grid_h = Preferences.values['GRIDSIZE_H']
        offset = self._offset
        # draw a background
        ctx.set_source_rgb(0.95, 0.95, 0.85)
        ctx.set_line_width(0.5)
        ctx.set_tolerance(0.1)
        ctx.set_line_join(cairo.LINE_JOIN_ROUND)
        ctx.new_path()
        ctx.rectangle(offset.x, offset.y, 3 * grid_w, 3 * grid_h)
        ctx.fill()
        # draw the gridlines
        # TODO use CSS for uniform colors?
        ctx.set_source_rgb(0.75, 0.75, 0.75)
        ctx.set_line_width(0.5)
        ctx.set_tolerance(0.1)
        ctx.set_line_join(cairo.LINE_JOIN_ROUND)

        x_max = offset.x + 3 * grid_w
        y_max = offset.y + 3 * grid_h

        # horizontal lines
        y = offset.y
        for count in range(4):
            ctx.new_path()
            ctx.move_to(offset.x, y)
            ctx.line_to(x_max, y)
            ctx.stroke()
            y += grid_h

        # vertical lines
        x = offset.x
        for count in range(4):
            ctx.new_path()
            ctx.move_to(x, offset.y)
            ctx.line_to(x, y_max)
            ctx.stroke()
            x += grid_w

    def draw_content(self, ctx):
        if self._matrix is None:
            return
        grid_w = Preferences.values['GRIDSIZE_W']
        grid_h = Preferences.values['GRIDSIZE_H']
        offset = self._offset
        ctx.set_source_rgb(0.1, 0.1, 0.1)
        use_pango_font = Preferences.values['PANGO_FONT']
        if use_pango_font:
            # https://sites.google.com/site/randomcodecollections/home/python-gtk-3-pango-cairo-example
            # https://developer.gnome.org/pango/stable/pango-Cairo-Rendering.html
            layout = PangoCairo.create_layout(ctx)
            desc = Pango.font_description_from_string(Preferences.values['FONT'])
            layout.set_font_description(desc)
        else:
            ctx.set_font_size(Preferences.values['FONTSIZE'])
            ctx.select_font_face("monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
        y = offset.y
        for r in self._matrix:
            x = offset.x
            for c in r:
                if use_pango_font:
                    ctx.move_to(x, y)
                    layout.set_text(str(c), -1)
                    PangoCairo.show_layout(ctx, layout)
                else:
                    # the Cairo text glyph origin is its left-bottom corner
                    ctx.move_to(x, y + Preferences.values['FONTSIZE'])
                    ctx.show_text(str(c))
                x += grid_w
            y += grid_h

    def draw_cursor(self, ctx):
        if not self.has_focus():
            return
        ctx.save()
        ctx.set_line_width(1.5)
        ctx.set_line_join(cairo.LINE_JOIN_ROUND)
        if self._cursor_on:
            ctx.set_source_rgb(0.75, 0.75, 0.75)
        else:
            ctx.set_source_rgb(0.5, 0.5, 0.5)
        x = self._hover_pos.x
        y = self._hover_pos.y
        ctx.rectangle(x, y, Preferences.values['GRIDSIZE_W'], Preferences.values['GRIDSIZE_H'])
        ctx.stroke()
        ctx.restore()

    def toggle_cursor(self, widget, frame_clock, user_data=None):
        now = time.time()
        elapsed = now - self.start_time
        if elapsed > 0.5:
            self.start_time = now
            self._cursor_on = not self._cursor_on
        self.queue_resize()
        return GLib.SOURCE_CONTINUE