Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPootle 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)
commitaf230d55f39879c6325d19ff0173b1b84dd4e028 (patch)
tree3975f6b83cd0a371ff630773bd7c738f766a38a0
parent1456c34a7cb4e806320ad7c8c002ac52e6616fe1 (diff)
parentab166ab1267eb1bdf67b968981583349db8ac27b (diff)
Merge branch 'master' of git.sugarlabs.org:xo-colors/mainline
-rw-r--r--NEWS8
-rw-r--r--XOEditorActivity.py28
-rw-r--r--activity/activity.info2
-rw-r--r--game.py143
-rw-r--r--icons/save-colors.svg58
-rw-r--r--sprites.py22
6 files changed, 225 insertions, 36 deletions
diff --git a/NEWS b/NEWS
index 0f285bf..def839e 100644
--- a/NEWS
+++ b/NEWS
@@ -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
diff --git a/game.py b/game.py
index 010380c..1dfd62e 100644
--- a/game.py
+++ b/game.py
@@ -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
diff --git a/sprites.py b/sprites.py
index 2b8bb55..081b456 100644
--- a/sprites.py
+++ b/sprites.py
@@ -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 '''