diff options
author | Pootle daemon <pootle@pootle.sugarlabs.org> | 2012-04-17 04:32:21 (GMT) |
---|---|---|
committer | Pootle daemon <pootle@pootle.sugarlabs.org> | 2012-04-17 04:32:21 (GMT) |
commit | af230d55f39879c6325d19ff0173b1b84dd4e028 (patch) | |
tree | 3975f6b83cd0a371ff630773bd7c738f766a38a0 | |
parent | 1456c34a7cb4e806320ad7c8c002ac52e6616fe1 (diff) | |
parent | ab166ab1267eb1bdf67b968981583349db8ac27b (diff) |
Merge branch 'master' of git.sugarlabs.org:xo-colors/mainline
-rw-r--r-- | NEWS | 8 | ||||
-rw-r--r-- | XOEditorActivity.py | 28 | ||||
-rw-r--r-- | activity/activity.info | 2 | ||||
-rw-r--r-- | game.py | 143 | ||||
-rw-r--r-- | icons/save-colors.svg | 58 | ||||
-rw-r--r-- | sprites.py | 22 |
6 files changed, 225 insertions, 36 deletions
@@ -1,9 +1,15 @@ NEWS +4 +* New button artwork +* Eliminated unnecessary rotate feature +* Sorted colors in spiral from "calm" to "exciting" +* Drag to repostion dots and XO man (positions saved in Journal) + 3 * Rebased on Python * Spiral layout of all color combinations * Saves new colors (in most Sugar environments) 2 -* initial release as a Javascript program run inside of Browse +* Initial release as a Javascript program run inside of Browse diff --git a/XOEditorActivity.py b/XOEditorActivity.py index ac17622..3358872 100644 --- a/XOEditorActivity.py +++ b/XOEditorActivity.py @@ -27,6 +27,7 @@ if _have_toolbox: from sugar.activity.widgets import StopButton from sugar.graphics.objectchooser import ObjectChooser from sugar.graphics.alert import ConfirmationAlert, NotifyAlert +from sugar.graphics.xocolor import colors from toolbar_utils import button_factory, radio_factory, separator_factory @@ -68,6 +69,15 @@ class XOEditorActivity(activity.Activity): self._game = Game(canvas, parent=self, mycolors=self.colors) + # Read the dot positions from the Journal + for i in range(len(colors)): + if 'x%d' % (i) in self.metadata and 'y%d' % (i) in self.metadata: + self._game.move_dot(i, int(self.metadata['x%d' % (i)]), + int(self.metadata['y%d' % (i)])) + if 'xox' in self.metadata and 'xoy' in self.metadata: + self._game.move_xo_man(int(self.metadata['xox']), + int(self.metadata['xoy'])) + def _setup_toolbars(self, have_toolbox): """ Setup the toolbars. """ @@ -96,15 +106,17 @@ class XOEditorActivity(activity.Activity): toolbox.set_current_toolbar(1) self.toolbar = games_toolbar + ''' _rotate_button = button_factory( 'view-refresh', self.toolbar, self._rotate_cb, tooltip=_('Rotate colors')) + ''' if _have_toolbox: separator_factory(toolbox.toolbar, True, False) self._save_colors_button = button_factory( - 'document-save', self.toolbar, self._save_colors_cb, + 'save-colors', self.toolbar, self._save_colors_cb, tooltip=_('Save colors')) if _have_toolbox: @@ -115,7 +127,7 @@ class XOEditorActivity(activity.Activity): def _save_colors_cb(self, button=None): ''' Save the new XO colors. ''' - ''' We warn the user if the plugin was previously loaded ''' + ''' We warn the user if they are going to save their selection ''' alert = ConfirmationAlert() alert.props.title = _('Saving colors') alert.props.msg = _('Do you want to save these colors?') @@ -140,7 +152,7 @@ class XOEditorActivity(activity.Activity): self._game.colors[0], self._game.colors[1])) alert = NotifyAlert() alert.props.title = _('Saving colors') - alert.props.msg = _('Restart required before new colors will appear.') + alert.props.msg = _('A restart is required before your new colors will appear.') def _notification_alert_response_cb(alert, response_id, self): self.remove_alert(alert) @@ -151,3 +163,13 @@ class XOEditorActivity(activity.Activity): def _rotate_cb(self, button=None): self._game.rotate() + + def write_file(self, file_path): + for i in range(len(colors)): + x, y = self._game.get_dot_xy(i) + self.metadata['x%d' % (i)] = str(x) + self.metadata['y%d' % (i)] = str(y) + + x, y = self._game.get_xo_man_xy() + self.metadata['xox'] = str(x) + self.metadata['xoy'] = str(y) diff --git a/activity/activity.info b/activity/activity.info index 0875555..ce76182 100644 --- a/activity/activity.info +++ b/activity/activity.info @@ -3,6 +3,6 @@ name = xoEditor exec = sugar-activity XOEditorActivity.XOEditorActivity
bundle_id = org.laptop.xoEditorActivity
icon = activity-xo-editor
-activity_version = 3
+activity_version = 4
show_launcher = yes
license=GPLv3
@@ -45,14 +45,22 @@ class Game(): self._parent = parent self._canvas.set_flags(gtk.CAN_FOCUS) - self._canvas.add_events(gtk.gdk.BUTTON_PRESS_MASK) self._canvas.connect("expose-event", self._expose_cb) + self._canvas.add_events(gtk.gdk.BUTTON_PRESS_MASK) self._canvas.connect("button-press-event", self._button_press_cb) + self._canvas.add_events(gtk.gdk.BUTTON_RELEASE_MASK) + self._canvas.connect('button-release-event', self._button_release_cb) + self._canvas.add_events(gtk.gdk.POINTER_MOTION_MASK) + self._canvas.connect("motion-notify-event", self._mouse_move_cb) self._width = gtk.gdk.screen_width() self._height = gtk.gdk.screen_height() - GRID_CELL_SIZE self._scale = self._width / 1200. + self.press = None + self.dragpos = [0, 0] + self.startpos = [0, 0] + self._dot_cache = {} self._xo_cache = {} @@ -62,7 +70,7 @@ class Game(): # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._dots = [] - self._xoman = None + self._xo_man = None self._generate_bg('#FFF') # First dot, starting angle @@ -74,7 +82,17 @@ class Game(): self._min = -self._dot_size_plus / 3 self._max = self._height - (self._dot_size_plus / 2.2) - self._generate_grid() + self._zones = [] + self._calc_zones() + self._generate_spiral() + + def _calc_zones(self): + for color in colors: + rgb1 = _from_hex(color[0]) + rgb2 = _from_hex(color[1]) + dv = _contrast(rgb1, rgb2) + dh = _delta_hue(rgb1, rgb2) + self._zones.append(_zone(dv, dh)) def _calc_next_dot_position(self): ''' calculate spiral coordinates ''' @@ -91,21 +109,34 @@ class Game(): if self._xy[1] < self._min or self._xy[1] > self._max: self._calc_next_dot_position() - def _generate_grid(self): - ''' Make a new set of dots for a grid of size edge ''' - _logger.debug('%d colors' % (len(colors))) - for i in range(len(colors)): - self._dots.append( - Sprite(self._sprites, self._xy[0], self._xy[1], - self._new_dot(colors[i]))) - self._dots[-1].type = i - self._calc_next_dot_position() - if self._xoman is None: + def _generate_spiral(self): + ''' Make a new set of dots for a sprial ''' + for z in range(4): + for i in range(len(colors)): + if self._zones[i] == z: + self._dots.append( + Sprite(self._sprites, self._xy[0], self._xy[1], + self._new_dot(colors[i]))) + self._dots[-1].type = i + self._calc_next_dot_position() + if self._xo_man is None: x = 510 * self._scale y = 280 * self._scale - self._xoman = Sprite(self._sprites, x, y, + self._xo_man = Sprite(self._sprites, x, y, self._new_xo_man(self.colors)) - self._xoman.type = None + self._xo_man.type = None + + def move_dot(self, i, x, y): + self._dots[i].move((x, y)) + + def get_dot_xy(self, i): + return self._dots[i].get_xy() + + def move_xo_man(self, x, y): + self._xo_man.move((x, y)) + + def get_xo_man_xy(self): + return self._xo_man.get_xy() def rotate(self): x, y = self._dots[0].get_xy() @@ -127,23 +158,41 @@ class Game(): def _button_press_cb(self, win, event): win.grab_focus() x, y = map(int, event.get_coords()) + self.dragpos = [x, y] spr = self._sprites.find_sprite((x, y)) - if spr == None: + if spr == None or spr == self._bg: return - - if type(spr.type) == int: - self.i = spr.type - _logger.debug('%d' % (self.i)) - self._new_surface() - else: - _logger.debug(type(spr.type)) + self.startpos = spr.get_xy() + self.press = spr + + def _mouse_move_cb(self, win, event): + """ Drag a rule with the mouse. """ + if self.press is None: + self.dragpos = [0, 0] + return True + win.grab_focus() + x, y = map(int, event.get_coords()) + dx = x - self.dragpos[0] + dy = y - self.dragpos[1] + self.press.move_relative((dx, dy)) + self.dragpos = [x, y] + + def _button_release_cb(self, win, event): + if self.press == None: + return True + if _distance(self.press.get_xy(), self.startpos) < 20: + if type(self.press.type) == int: + self.i = self.press.type + self._new_surface() + self.press.move(self.startpos) + self.press = None def _new_surface(self): self.colors[0] = colors[self.i][0] self.colors[1] = colors[self.i][1] - self._xoman.set_image(self._new_xo_man(colors[self.i])) - self._xoman.set_layer(100) + self._xo_man.set_image(self._new_xo_man(colors[self.i])) + self._xo_man.set_layer(100) def _expose_cb(self, win, event): self.do_expose_event(event) @@ -280,6 +329,7 @@ fill="%s" stroke="%s" stroke-width="%f" visibility="visible" />' % ( def _footer(self): return '</svg>\n' + def svg_str_to_pixbuf(svg_string): """ Load pixbuf from SVG string """ pl = gtk.gdk.PixbufLoader('svg') @@ -289,5 +339,48 @@ def svg_str_to_pixbuf(svg_string): return pixbuf +def _from_hex(num): + r = float.fromhex('0x' + num[1:3]) + g = float.fromhex('0x' + num[3:5]) + b = float.fromhex('0x' + num[5:]) + return [r, g, b] + + def _to_hex(rgb): return('#%02x%02x%02x' % (rgb[0], rgb[1], rgb[2])) + + +def _contrast(rgb1, rgb2): + v1 = float(rgb1[0]) * 0.3 + float(rgb1[1]) * 0.6 + float(rgb1[2]) * 0.1 + v2 = float(rgb2[0]) * 0.3 + float(rgb2[1]) * 0.6 + float(rgb2[2]) * 0.1 + return abs(v2 - v1) + + +def _hue(rgb): + a = 0.5 * (2.0 * rgb[0] - rgb[1] - rgb[2]) + b = 0.87 * (rgb[1] - rgb[2]) + h = atan2(b, a) + return h * 180 / pi + + +def _delta_hue(rgb1, rgb2): + h1 = _hue(rgb1) + h2 = _hue(rgb2) + return abs(h2 - h1) + + +def _zone(dv, dh): + if dh < 75: + zone = 0 + elif dh > 150: + zone = 1 + else: + zone = 2 + if dv > 48: + zone += 1 + return zone + + +def _distance(pos1, pos2): + return sqrt((pos1[0] - pos2[0]) * (pos1[0] - pos2[0]) + \ + (pos1[1] - pos2[1]) * (pos1[1] - pos2[1])) diff --git a/icons/save-colors.svg b/icons/save-colors.svg new file mode 100644 index 0000000..d0fe05c --- /dev/null +++ b/icons/save-colors.svg @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + version="1.1" + width="55" + height="55" + viewBox="0 0 55 55" + id="Layer_1" + xml:space="preserve"><metadata + id="metadata36"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs + id="defs34"> + + + + + + + + + + + + </defs> +<g + transform="translate(-0.41999817,0.062)" + id="g26"> + <line + x1="41.169998" + x2="52.441002" + y1="16.188" + y2="4.9169998" + id="line28" + style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round" /> + <polyline + points=" 51.562,15.306 41.17,16.188 42.053,5.794 " + id="polyline30" + style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round" /> + </g> +<g + transform="matrix(0.8671392,0,0,0.8671392,-5.3229905,6.1528047)" + id="stock-xo_1_" + style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-opacity:1;display:block"><path + d="m 33.233,35.1 10.102,10.1 c 0.752,0.75 1.217,1.783 1.217,2.932 0,2.287 -1.855,4.143 -4.146,4.143 -1.145,0 -2.178,-0.463 -2.932,-1.211 l -10.102,-10.103 -10.1,10.1 c -0.75,0.75 -1.787,1.211 -2.934,1.211 -2.284,0 -4.143,-1.854 -4.143,-4.141 0,-1.146 0.465,-2.184 1.212,-2.934 L 21.511,35.095 11.409,24.995 c -0.747,-0.748 -1.212,-1.785 -1.212,-2.93 0,-2.289 1.854,-4.146 4.146,-4.146 1.143,0 2.18,0.465 2.93,1.214 L 27.372,29.235 37.474,19.132 c 0.754,-0.749 1.787,-1.214 2.934,-1.214 2.289,0 4.146,1.856 4.146,4.145 0,1.146 -0.467,2.18 -1.217,2.932 L 33.233,35.1 z" + id="path3082" + style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:3.5;stroke-opacity:1" /><circle + cx="27.371" + cy="10.849" + r="8.1219997" + id="circle3084" + style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:3.5;stroke-opacity:1" /></g></svg>
\ No newline at end of file @@ -120,18 +120,22 @@ class Sprites: else: self.list.insert(i, spr) + def find_in_list(self, spr): + if spr in self.list: + return True + return False + def remove_from_list(self, spr): ''' Remove a sprite from the list. ''' if spr in self.list: self.list.remove(spr) - def find_sprite(self, pos, inverse=False): + def find_sprite(self, pos, region=False): ''' Search based on (x, y) position. Return the 'top/first' one. ''' list = self.list[:] - if not inverse: - list.reverse() + list.reverse() for spr in list: - if spr.hit(pos): + if spr.hit(pos, readpixel=not region): return spr return None @@ -346,7 +350,7 @@ class Sprite: if len(self.labels) > 0: self.draw_label(cr) - def hit(self, pos): + def hit(self, pos, readpixel=False): ''' Is (x, y) on top of the sprite? ''' x, y = pos if x < self.rect.x: @@ -357,7 +361,13 @@ class Sprite: return False if y > self.rect.y + self.rect.height: return False - return True + if readpixel: + r, g, b, a = self.get_pixel(pos) + if r == g == b == a == 0: + return False + if a == -1: + return False + return self._sprites.find_in_list(self) def draw_label(self, cr): ''' Draw the label based on its attributes ''' |