<br><br><div class="gmail_quote">On Sat, Jul 7, 2012 at 7:01 PM, Daniel Francis <span dir="ltr"><<a href="mailto:francis@sugarlabs.org" target="_blank">francis@sugarlabs.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Now the activity hasn't got HippoCanvas and I'm ready to port it to Gtk3!<br>
<br>
A little comment, I added de _share function because I forgot it at the time to remove the early toolkit modules, and it appeared in the log when I tried to share the activity.<br>
<br>
Signed-off-by: Daniel Francis <<a href="mailto:francis@sugarlabs.org">francis@sugarlabs.org</a>><br>
---<br>
activity.py | 12 ++<br>
chat.py | 137 +++++++++++-----------<br>
chatbox.py | 364 ++++++++++++++++++++++++++++++++++-------------------------<br>
roundbox.py | 100 ++++++++++++++++<br>
4 files changed, 391 insertions(+), 222 deletions(-)<br>
create mode 100644 roundbox.py<br>
<br>
diff --git a/activity.py b/activity.py<br>
index a3f01ac..d4f0e2a 100644<br>
--- a/activity.py<br>
+++ b/activity.py<br>
@@ -203,6 +203,18 @@ class SpeakActivity(SharedActivity):<br>
toolbox.show_all()<br>
self.toolbar_box = toolbox<br>
<br>
+ def _share(self, tube_conn, initiator):<br>
+ logging.debug('Activity._share state=%s' % self.__state)<br>
+<br>
+ if self.__state == _NEW_INSTANCE:<br>
+ self.__postponed_share.append((tube_conn, initiator))<br>
+ self.__state = _PRE_INSTANCE<br>
+ elif self.__state == _PRE_INSTANCE:<br>
+ self.__postponed_share.append((tube_conn, initiator))<br>
+ self.__instance()<br>
+ elif self.__state == _POST_INSTANCE:<br>
+ self.share_instance(tube_conn, initiator)<br>
+<br>
def set_cursor(self, cursor):<br>
if not isinstance(cursor, gtk.gdk.Cursor):<br>
cursor = CursorFactory().get_cursor(cursor)<br>
diff --git a/chat.py b/chat.py<br>
index 12d0075..a61822d 100644<br>
--- a/chat.py<br>
+++ b/chat.py<br>
@@ -16,12 +16,11 @@<br>
<br>
import gtk<br>
import pango<br>
-import hippo<br>
import logging<br>
from gettext import gettext as _<br>
<br>
import sugar.graphics.style as style<br>
-from sugar.graphics.roundbox import CanvasRoundBox<br>
+from roundbox import RoundBox<br>
from sugar.graphics.toggletoolbutton import ToggleToolButton<br>
<br>
import eye<br>
@@ -45,9 +44,9 @@ ENTRY_XPAD = 0<br>
ENTRY_YPAD = 7<br>
<br>
<br>
-class View(hippo.Canvas):<br>
+class View(gtk.EventBox):<br>
def __init__(self):<br>
- hippo.Canvas.__init__(self)<br>
+ gtk.EventBox.__init__(self)<br>
<br>
self.messenger = None<br>
<a href="http://self.me" target="_blank">self.me</a> = None<br>
@@ -57,16 +56,18 @@ class View(hippo.Canvas):<br>
<br>
# buddies box<br>
<br>
- self._buddies_list = hippo.CanvasBox(<br>
- background_color=BUDDIES_COLOR.get_int(),<br>
- box_width=BUDDIES_WIDTH,<br>
- padding=ENTRY_YPAD,<br>
- spacing=ENTRY_YPAD)<br>
+ self._buddies_list = gtk.VBox()<br>
+ self._buddies_list.set_homogeneous(False)<br>
+ self._buddies_list.props.spacing = ENTRY_YPAD<br>
<br>
- self._buddies_box = hippo.CanvasScrollbars()<br>
- self._buddies_box.set_policy(hippo.ORIENTATION_HORIZONTAL,<br>
- hippo.SCROLLBAR_NEVER)<br>
- self._buddies_box.set_root(self._buddies_list)<br>
+ self._buddies_box = gtk.ScrolledWindow()<br>
+ self._buddies_box.set_policy(gtk.POLICY_ALWAYS,<br>
+ gtk.POLICY_NEVER)<br>
+ evbox = gtk.EventBox()<br>
+ evbox.modify_bg(gtk.STATE_NORMAL, BUDDIES_COLOR.get_gdk_color())<br>
+ evbox.add(self._buddies_list)<br>
+ evbox.show()<br>
+ self._buddies_box.add_with_viewport(evbox)<br>
<br>
# chat entry<br>
<br>
@@ -82,40 +83,37 @@ class View(hippo.Canvas):<br>
chat_post.connect('key-press-event', self._key_press_cb)<br>
chat_post.props.wrap_mode = gtk.WRAP_WORD_CHAR<br>
chat_post.set_size_request(-1, BUDDY_SIZE - ENTRY_YPAD * 2)<br>
- chat_post_box = CanvasRoundBox(<br>
- background_color=style.COLOR_WHITE.get_int(),<br>
- padding_left=ENTRY_XPAD,<br>
- padding_right=ENTRY_XPAD,<br>
- padding_top=ENTRY_YPAD,<br>
- padding_bottom=ENTRY_YPAD<br>
- )<br>
- chat_post_box.props.border_color = ENTRY_COLOR.get_int()<br>
- chat_post_box.append(hippo.CanvasWidget(widget=chat_post),<br>
- hippo.PACK_EXPAND)<br>
-<br>
- chat_entry = CanvasRoundBox(background_color=ENTRY_COLOR.get_int(),<br>
- padding_left=ENTRY_XPAD,<br>
- padding_right=ENTRY_XPAD,<br>
- padding_top=ENTRY_YPAD,<br>
- padding_bottom=ENTRY_YPAD,<br>
- spacing=ENTRY_YPAD)<br>
- chat_entry.props.orientation = hippo.ORIENTATION_HORIZONTAL<br>
- chat_entry.props.border_color = style.COLOR_WHITE.get_int()<br>
- chat_entry.append(my_face_widget)<br>
- chat_entry.append(chat_post_box, hippo.PACK_EXPAND)<br>
-<br>
- chat_box = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL,<br>
- background_color=style.COLOR_WHITE.get_int())<br>
- chat_box.append(self._chat, hippo.PACK_EXPAND)<br>
- chat_box.append(chat_entry)<br>
+ chat_post_box = RoundBox()<br>
+ chat_post_box.background_color = style.COLOR_WHITE<br>
+ chat_post_box.border_color = ENTRY_COLOR<br>
+ chat_post_box.pack_start(chat_post, True, True, ENTRY_XPAD)<br>
+<br>
+ chat_entry = RoundBox()<br>
+ chat_entry.set_border_width(ENTRY_YPAD)<br>
+ chat_entry.background_color = ENTRY_COLOR<br>
+ chat_entry.border_color = style.COLOR_WHITE<br>
+ chat_entry.pack_start(my_face_widget, False, True, 0)<br>
+ separator = gtk.EventBox()<br>
+ separator.modify_bg(gtk.STATE_NORMAL, ENTRY_COLOR.get_gdk_color())<br>
+ separator.set_size_request(ENTRY_YPAD, -1)<br>
+ separator.show()<br>
+ chat_entry.pack_start(separator, False, False)<br>
+ chat_entry.pack_start(chat_post_box, True, True, ENTRY_XPAD)<br>
+<br>
+ evbox = gtk.EventBox()<br>
+ evbox.modify_bg(gtk.STATE_NORMAL, style.COLOR_WHITE.get_gdk_color())<br>
+ chat_box = gtk.VBox()<br>
+ chat_box.pack_start(self._chat, True, True)<br>
+ chat_box.pack_start(chat_entry, False, True)<br>
+ evbox.add(chat_box)<br>
<br>
# desk<br>
<br>
- self._desk = hippo.CanvasBox()<br>
- self._desk.props.orientation = hippo.ORIENTATION_HORIZONTAL<br>
- self._desk.append(chat_box, hippo.PACK_EXPAND)<br>
+ self._desk = gtk.HBox()<br>
+ self._desk.pack_start(evbox, True, True)<br>
+ self._desk.show_all()<br>
<br>
- self.set_root(self._desk)<br>
+ self.add(self._desk)<br>
<br>
def update(self, status):<br>
self.me.update(status)<br>
@@ -160,34 +158,30 @@ class View(hippo.Canvas):<br>
self.me.shut_up()<br>
<br>
def _add_buddy(self, buddy):<br>
- box = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL,<br>
- background_color=BUDDIES_COLOR.get_int(),<br>
- spacing=ENTRY_YPAD)<br>
+ evbox = gtk.EventBox()<br>
+ evbox.modify_bg(gtk.STATE_NORMAL, BUDDIES_COLOR.get_gdk_color())<br>
+ box = gtk.HBox()<br>
<br>
buddy_face, buddy_widget = self._new_face(buddy, BUDDIES_COLOR)<br>
<br>
- char_box = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL)<br>
- nick = hippo.CanvasText(text=buddy.props.nick,<br>
- xalign=hippo.ALIGNMENT_START,<br>
- yalign=hippo.ALIGNMENT_START)<br>
- lang = hippo.CanvasText(text='',<br>
- xalign=hippo.ALIGNMENT_START,<br>
- yalign=hippo.ALIGNMENT_START)<br>
- char_box.append(nick)<br>
- char_box.append(lang)<br>
+ char_box = gtk.VBox()<br>
+ nick = gtk.Label(buddy.props.nick)<br>
+ lang = gtk.Label()<br>
+ char_box.pack_start(nick)<br>
+ char_box.pack_start(lang)<br>
<br>
- box.append(buddy_widget)<br>
- box.append(char_box, hippo.PACK_EXPAND)<br>
+ box.pack_start(buddy_widget, False, False, ENTRY_YPAD)<br>
+ box.pack_start(char_box, True, True, ENTRY_YPAD)<br>
<br>
self._buddies[buddy] = {<br>
'box': box,<br>
'face': buddy_face,<br>
'lang': lang<br>
}<br>
- self._buddies_list.append(box)<br>
+ self._buddies_list.pack_start(box)<br>
<br>
if len(self._buddies) == 1:<br>
- self._desk.append(self._buddies_box)<br>
+ self._desk.pack_start(self._buddies_box)<br>
<br>
def _key_press_cb(self, widget, event):<br>
if event.keyval == gtk.keysyms.Return:<br>
@@ -213,17 +207,20 @@ class View(hippo.Canvas):<br>
buddy_face = face.View(fill_color)<br>
buddy_face.show_all()<br>
<br>
- inner = CanvasRoundBox(background_color=fill_color.get_int())<br>
- inner.props.border_color = fill_color.get_int()<br>
- inner.append(hippo.CanvasWidget(widget=buddy_face), hippo.PACK_EXPAND)<br>
- inner.props.border = BUDDY_PAD<br>
-<br>
- outer = CanvasRoundBox(background_color=stroke_color.get_int(),<br>
- box_width=BUDDY_SIZE,<br>
- box_height=BUDDY_SIZE)<br>
- outer.props.border_color = stroke_color.get_int()<br>
- outer.append(inner, hippo.PACK_EXPAND)<br>
- outer.props.border = BUDDY_PAD<br>
+ inner = RoundBox()<br>
+ inner.set_border_width(BUDDY_PAD)<br>
+ inner.background_color = fill_color<br>
+ inner.border_color = fill_color<br>
+ inner.pack_start(buddy_face, True, True, 0)<br>
+ inner.border = BUDDY_PAD<br>
+<br>
+ outer = RoundBox()<br>
+ outer.set_border_width(BUDDY_PAD)<br>
+ outer.background_color = stroke_color<br>
+ outer.set_size_request(BUDDY_SIZE, BUDDY_SIZE)<br>
+ outer.border_color = stroke_color<br>
+ outer.pack_start(inner, True, True, 0)<br>
+ outer.border = BUDDY_PAD<br>
<br>
return (buddy_face, outer)<br>
<br>
diff --git a/chatbox.py b/chatbox.py<br>
index 671ba09..58320b4 100644<br>
--- a/chatbox.py<br>
+++ b/chatbox.py<br>
@@ -17,21 +17,20 @@<br>
# This code is a stripped down version of the Chat<br>
<br>
import gtk<br>
-import hippo<br>
import logging<br>
import pango<br>
import re<br>
from datetime import datetime<br>
-from gobject import SIGNAL_RUN_FIRST, TYPE_PYOBJECT<br>
from gettext import gettext as _<br>
<br>
-import sugar.graphics.style as style<br>
-from sugar.graphics.roundbox import CanvasRoundBox<br>
-from sugar.graphics.palette import Palette, CanvasInvoker<br>
+from sugar.graphics.style import style<br>
+from sugar.graphics.palette import Palette<br>
+from sugar.graphics.palette import CanvasInvoker<br>
+from sugar.graphics.palette import MouseSpeedDetector<br>
from sugar.presence import presenceservice<br>
-from sugar.graphics.style import (Color, COLOR_BLACK, COLOR_WHITE)<br>
from sugar.graphics.menuitem import MenuItem<br>
from sugar.activity.activity import get_activity_root<br>
+from roundbox import RoundBox<br>
<br>
logger = logging.getLogger('speak')<br>
<br>
@@ -40,9 +39,173 @@ URL_REGEXP = re.compile('((http|ftp)s?://)?'<br>
'(:[1-9][0-9]{0,4})?(/[-a-zA-Z0-9/%~@&_+=;:,.?#]*[a-zA-Z0-9/])?')<br>
<br>
<br>
-class ChatBox(hippo.CanvasScrollbars):<br>
+class TextBox(gtk.TextView):<br>
+<br>
+ hand_cursor = gtk.gdk.Cursor(gtk.gdk.HAND2)<br>
+<br>
+ def __init__(self, color, bg_color, lang_rtl):<br>
+ self._lang_rtl = lang_rtl<br>
+ gtk.TextView.__init__(self)<br>
+ self.set_editable(False)<br>
+ self.set_cursor_visible(False)<br>
+ self.set_wrap_mode(gtk.WRAP_WORD_CHAR)<br>
+ self.get_buffer().set_text("", 0)<br>
+ self.iter_text = self.get_buffer().get_iter_at_offset(0)<br>
+ self.fg_tag = self.get_buffer().create_tag("foreground_color",<br>
+ foreground=color.get_html())<br>
+ self._subscript_tag = self.get_buffer().create_tag('subscript',<br>
+ rise=-7 * pango.SCALE) # in pixels<br>
+ self._empty = True<br>
+ self.palette = None<br>
+ self._mouse_detector = MouseSpeedDetector(self, 200, 5)<br>
+ self._mouse_detector.connect('motion-slow', self.__mouse_slow_cb)<br>
+ self.modify_base(gtk.STATE_NORMAL, bg_color.get_gdk_color())<br>
+<br>
+ self.add_events(gtk.gdk.POINTER_MOTION_MASK | \<br>
+ gtk.gdk.BUTTON_PRESS_MASK | \<br>
+ gtk.gdk.BUTTON_RELEASE_MASK | \<br>
+ gtk.gdk.LEAVE_NOTIFY_MASK)<br>
+<br>
+ self.connect('event-after', self.__event_after_cb)<br>
+ self.connect('button-press-event', self.__button_press_cb)<br>
+ self.motion_notify_id = self.connect('motion-notify-event', \<br>
+ self.__motion_notify_cb)<br>
+ self.connect('visibility-notify-event', self.__visibility_notify_cb)<br>
+ self.connect('leave-notify-event', self.__leave_notify_event_cb)<br>
+<br>
+ def __leave_notify_event_cb(self, widget, event):<br>
+ self._mouse_detector.stop()<br>
+<br>
+ def __button_press_cb(self, widget, event):<br>
+ if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3:<br>
+ # To disable the standard textview popup<br>
+ return True<br>
+<br>
+ # Links can be activated by clicking.<br>
+ def __event_after_cb(self, widget, event):<br>
+ if event.type != gtk.gdk.BUTTON_RELEASE:<br>
+ return False<br>
+<br>
+ x, y = self.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET,<br>
+ int(event.x), int(event.y))<br>
+ iter_tags = self.get_iter_at_location(x, y)<br>
+<br>
+ for tag in iter_tags.get_tags():<br>
+ url = tag.get_data('url')<br>
+ if url is not None:<br>
+ if event.button == 3:<br>
+ palette = tag.get_data('palette')<br>
+ xw, yw = self.get_toplevel().get_pointer()<br>
+ palette.move(int(xw), int(yw))<br>
+ palette.popup()<br>
+ else:<br>
+ self._show_via_journal(url)<br>
+ break<br>
+<br>
+ return False<br>
+<br>
+ def check_url_hovering(self, x, y):<br>
+ # Looks at all tags covering the position (x, y) in the text view,<br>
+ # and if one of them is a link return True<br>
+<br>
+ hovering = False<br>
+ # When check on_slow_mouse event, the position can be out<br>
+ # of the widget and return negative values.<br>
+ if x < 0 or y < 0:<br>
+ return hovering<br>
+<br>
+ self.palette = None<br>
+ iter_tags = self.get_iter_at_location(x, y)<br>
+<br>
+ tags = iter_tags.get_tags()<br>
+ for tag in tags:<br>
+ url = tag.get_data('url')<br>
+ self.palette = tag.get_data('palette')<br>
+ if url is not None:<br>
+ hovering = True<br>
+ break<br>
+ return hovering<br>
+<br>
+ def set_cursor_if_appropriate(self, x, y):<br>
+ # Looks at all tags covering the position (x, y) in the text view,<br>
+ # and if one of them is a link, change the cursor to the "hands" cursor<br>
+<br>
+ hovering_over_link = self.check_url_hovering(x, y)<br>
+ win = self.get_window(gtk.TEXT_WINDOW_TEXT)<br>
+ if hovering_over_link:<br>
+ win.set_cursor(self.hand_cursor)<br>
+ self._mouse_detector.start()<br>
+ else:<br>
+ win.set_cursor(None)<br>
+ self._mouse_detector.stop()<br>
+<br>
+ def __mouse_slow_cb(self, widget):<br>
+ x, y = self.get_pointer()<br>
+ hovering_over_link = self.check_url_hovering(x, y)<br>
+ if hovering_over_link:<br>
+ if self.palette is not None:<br>
+ xw, yw = self.get_toplevel().get_pointer()<br>
+ self.palette.move(xw, yw)<br>
+ self.palette.popup()<br>
+ self._mouse_detector.stop()<br>
+ else:<br>
+ if self.palette is not None:<br>
+ self.palette.popdown()<br>
+<br>
+ # Update the cursor image if the pointer moved.<br>
+ def __motion_notify_cb(self, widget, event):<br>
+ x, y = self.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET,<br>
+ int(event.x), int(event.y))<br>
+ self.set_cursor_if_appropriate(x, y)<br>
+ self.window.get_pointer()<br>
+ return False<br>
+<br>
+ def __visibility_notify_cb(self, widget, event):<br>
+ # Also update the cursor image if the window becomes visible<br>
+ # (e.g. when a window covering it got iconified).<br>
+<br>
+ wx, wy, __ = self.window.get_pointer()<br>
+ bx, by = self.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET, wx, wy)<br>
+ self.set_cursor_if_appropriate(bx, by)<br>
+ return False<br>
+<br>
+ def __palette_mouse_enter_cb(self, widget, event):<br>
+ self.handler_block(self.motion_notify_id)<br>
+<br>
+ def __palette_mouse_leave_cb(self, widget, event):<br>
+ self.handler_unblock(self.motion_notify_id)<br>
+<br>
+ def add_text(self, text):<br>
+ buf = self.get_buffer()<br>
+<br>
+ if not self._empty:<br>
+ buf.insert(self.iter_text, '\n')<br>
+<br>
+ words = text.split()<br>
+ for word in words:<br>
+ buf.insert(self.iter_text, word)<br>
+ buf.insert(self.iter_text, ' ')<br>
+<br>
+ self._empty = False<br>
+<br>
+<br>
+class ColorLabel(gtk.Label):<br>
+<br>
+ def __init__(self, text, color=None):<br>
+ self._color = color<br>
+ if self._color is not None:<br>
+ text = '<span foreground="%s">' % self._color.get_html() +\<br>
+ text + '</span>'<br>
+ gtk.Label.__init__(self)<br>
+ self.set_use_markup(True)<br>
+ self.set_markup(text)<br>
+ self.props.selectable = True<br>
+<br>
+<br>
+class ChatBox(gtk.ScrolledWindow):<br>
+<br>
def __init__(self):<br>
- hippo.CanvasScrollbars.__init__(self)<br>
+ gtk.ScrolledWindow.__init__(self)<br>
<br>
self.owner = presenceservice.get_instance().get_owner()<br>
<br>
@@ -54,15 +217,17 @@ class ChatBox(hippo.CanvasScrollbars):<br>
self._last_msg = None<br>
self._chat_log = ''<br>
<br>
- self._conversation = hippo.CanvasBox(<br>
- spacing=0,<br>
- background_color=COLOR_WHITE.get_int())<br>
-<br>
- self.set_policy(hippo.ORIENTATION_HORIZONTAL,<br>
- hippo.SCROLLBAR_NEVER)<br>
- self.set_root(self._conversation)<br>
-<br>
- vadj = self.props.widget.get_vadjustment()<br>
+ self._conversation = gtk.VBox()<br>
+ self._conversation.set_homogeneous(False)<br>
+ self._conversation.props.spacing = style.LINE_WIDTH<br>
+ self._conversation.props.border_width = style.LINE_WIDTH<br>
+ evbox = gtk.EventBox()<br>
+ evbox.modify_bg(gtk.STATE_NORMAL, style.COLOR_WHITE.get_gdk_color())<br>
+ evbox.add(self._conversation)<br>
+<br>
+ self.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)<br>
+ self.add_with_viewport(evbox)<br>
+ vadj = self.get_vadjustment()<br>
vadj.connect('changed', self._scroll_changed_cb)<br>
vadj.connect('value-changed', self._scroll_value_changed_cb)<br>
<br>
@@ -80,17 +245,12 @@ class ChatBox(hippo.CanvasScrollbars):<br>
False: show what buddy said<br>
True: show what buddy did<br>
<br>
- hippo layout:<br>
.------------- rb ---------------.<br>
- | +name_vbox+ +----msg_vbox----+ |<br>
+ | +name_vbox+ +----align-----+ |<br>
| | | | | |<br>
- | | nick: | | +--msg_hbox--+ | |<br>
- | | | | | text | | |<br>
+ | | nick: | | +--message---+ | |<br>
+ | | | | | text | | |<br>
| +---------+ | +------------+ | |<br>
- | | | |<br>
- | | +--msg_hbox--+ | |<br>
- | | | text | url | | |<br>
- | | +------------+ | |<br>
| +----------------+ |<br>
`--------------------------------'<br>
"""<br>
@@ -110,16 +270,15 @@ class ChatBox(hippo.CanvasScrollbars):<br>
color_stroke_html, color_fill_html = ('#000000', '#888888')<br>
<br>
# Select text color based on fill color:<br>
- color_fill_rgba = Color(color_fill_html).get_rgba()<br>
+ color_fill_rgba = style.Color(color_fill_html).get_rgba()<br>
color_fill_gray = (color_fill_rgba[0] + color_fill_rgba[1] +<br>
color_fill_rgba[2]) / 3<br>
- color_stroke = Color(color_stroke_html).get_int()<br>
- color_fill = Color(color_fill_html).get_int()<br>
-<br>
+ color_stroke = style.Color(color_stroke_html)<br>
+ color_fill = style.Color(color_fill_html)<br>
if color_fill_gray < 0.5:<br>
- text_color = COLOR_WHITE.get_int()<br>
+ text_color = style.COLOR_WHITE<br>
else:<br>
- text_color = COLOR_BLACK.get_int()<br>
+ text_color = style.COLOR_BLACK<br>
<br>
self._add_log(nick, color, text, status_message)<br>
<br>
@@ -139,82 +298,35 @@ class ChatBox(hippo.CanvasScrollbars):<br>
<br>
if not new_msg:<br>
rb = self._last_msg<br>
- msg_vbox = rb.get_children()[1]<br>
- msg_hbox = hippo.CanvasBox(<br>
- orientation=hippo.ORIENTATION_HORIZONTAL)<br>
- msg_vbox.append(msg_hbox)<br>
+ self._last_msg.add_text(text)<br>
else:<br>
- rb = CanvasRoundBox(background_color=color_fill,<br>
- border_color=color_stroke,<br>
- padding=4)<br>
- rb.props.border_color = color_stroke # Bug #3742<br>
- self._last_msg = rb<br>
+ rb = RoundBox()<br>
+ screen_width = gtk.gdk.screen_width()<br>
+ # keep space to the scrollbar<br>
+ rb.set_size_request(screen_width - 50, -1)<br>
+ rb.props.border_width = style.DEFAULT_PADDING<br>
+ rb.props.spacing = style.DEFAULT_SPACING<br>
+ rb.background_color = color_fill<br>
+ rb.border_color = color_stroke<br>
self._last_msg_sender = buddy<br>
+ self._last_msg = rb<br>
if not status_message:<br>
- name = hippo.CanvasText(text=nick + ': ',<br>
- color=text_color)<br>
- name_vbox = hippo.CanvasBox(<br>
- orientation=hippo.ORIENTATION_VERTICAL)<br>
- name_vbox.append(name)<br>
- rb.append(name_vbox)<br>
- msg_vbox = hippo.CanvasBox(<br>
- orientation=hippo.ORIENTATION_VERTICAL)<br>
- rb.append(msg_vbox)<br>
- msg_hbox = hippo.CanvasBox(<br>
- orientation=hippo.ORIENTATION_HORIZONTAL)<br>
- msg_vbox.append(msg_hbox)<br>
+ name = ColorLabel(text=nick + ':', color=text_color)<br>
+ name_vbox = gtk.VBox()<br>
+ name_vbox.pack_start(name, False, False)<br>
+ rb.pack_start(name_vbox, False, False)<br>
+ message = TextBox(text_color, color_fill, lang_rtl)<br>
+ vbox = gtk.VBox()<br>
+ vbox.pack_start(message, True, True)<br>
+ rb.pack_start(vbox, True, True)<br>
+ self._last_msg = message<br>
+ self._conversation.pack_start(rb, False, False)<br>
+ message.add_text(text)<br>
+ self._conversation.show_all()<br>
<br>
if status_message:<br>
self._last_msg_sender = None<br>
<br>
- match = URL_REGEXP.match(text)<br>
- while match:<br>
- # there is a URL in the text<br>
- starttext = text[:match.start()]<br>
- if starttext:<br>
- message = hippo.CanvasText(<br>
- text=starttext,<br>
- size_mode=hippo.CANVAS_SIZE_WRAP_WORD,<br>
- color=text_color,<br>
- xalign=hippo.ALIGNMENT_START)<br>
- msg_hbox.append(message)<br>
- url = text[match.start():match.end()]<br>
-<br>
- message = CanvasLink(<br>
- text=url,<br>
- color=text_color)<br>
- attrs = pango.AttrList()<br>
- attrs.insert(pango.AttrUnderline(pango.UNDERLINE_SINGLE, 0, 32767))<br>
- message.set_property("attributes", attrs)<br>
- message.connect('activated', self._link_activated_cb)<br>
-<br>
- # call interior magic which should mean just:<br>
- # CanvasInvoker().parent = message<br>
- CanvasInvoker(message)<br>
-<br>
- msg_hbox.append(message)<br>
- text = text[match.end():]<br>
- match = URL_REGEXP.search(text)<br>
-<br>
- if text:<br>
- message = hippo.CanvasText(<br>
- text=text,<br>
- size_mode=hippo.CANVAS_SIZE_WRAP_WORD,<br>
- color=text_color,<br>
- xalign=hippo.ALIGNMENT_START)<br>
- msg_hbox.append(message)<br>
-<br>
- # Order of boxes for RTL languages:<br>
- if lang_rtl:<br>
- msg_hbox.reverse()<br>
- if new_msg:<br>
- rb.reverse()<br>
-<br>
- if new_msg:<br>
- box = hippo.CanvasBox(padding=2)<br>
- box.append(rb)<br>
- self._conversation.append(box)<br>
-<br>
def _scroll_value_changed_cb(self, adj, scroll=None):<br>
"""Turn auto scrolling on or off.<br>
If the user scrolled up, turn it off.<br>
@@ -282,58 +394,6 @@ class ChatBox(hippo.CanvasScrollbars):<br>
nick, color, status_message, text)<br>
<br>
<br>
-class CanvasLink(hippo.CanvasLink):<br>
- def __init__(self, **kwargs):<br>
- hippo.CanvasLink.__init__(self, **kwargs)<br>
-<br>
- def create_palette(self):<br>
- return URLMenu(self.props.text)<br>
-<br>
-<br>
-class URLMenu(Palette):<br>
- def __init__(self, url):<br>
- Palette.__init__(self, url)<br>
-<br>
- self.url = url_check_protocol(url)<br>
-<br>
- menu_item = MenuItem(_('Copy to Clipboard'), 'edit-copy')<br>
- menu_item.connect('activate', self._copy_to_clipboard_cb)<br>
- self.menu.append(menu_item)<br>
- menu_item.show()<br>
-<br>
- def create_palette(self):<br>
- pass<br>
-<br>
- def _copy_to_clipboard_cb(self, menuitem):<br>
- logger.debug('Copy %s to clipboard', self.url)<br>
- clipboard = gtk.clipboard_get()<br>
- targets = [("text/uri-list", 0, 0),<br>
- ("UTF8_STRING", 0, 1)]<br>
-<br>
- if not clipboard.set_with_data(targets,<br>
- self._clipboard_data_get_cb,<br>
- self._clipboard_clear_cb,<br>
- (self.url)):<br>
- logger.error('GtkClipboard.set_with_data failed!')<br>
- else:<br>
- self.owns_clipboard = True<br>
-<br>
- def _clipboard_data_get_cb(self, clipboard, selection, info, data):<br>
- logger.debug('_clipboard_data_get_cb data=%s target=%s', data,<br>
- selection.target)<br>
- if selection.target in ['text/uri-list']:<br>
- if not selection.set_uris([data]):<br>
- logger.debug('failed to set_uris')<br>
- else:<br>
- logger.debug('not uri')<br>
- if not selection.set_text(data):<br>
- logger.debug('failed to set_text')<br>
-<br>
- def _clipboard_clear_cb(self, clipboard, data):<br>
- logger.debug('clipboard_clear_cb')<br>
- self.owns_clipboard = False<br>
-<br>
-<br>
def url_check_protocol(url):<br>
"""Check that the url has a protocol, otherwise prepend https://<br>
<br>
diff --git a/roundbox.py b/roundbox.py<br>
new file mode 100644<br>
index 0000000..35d5f7c<br>
--- /dev/null<br>
+++ b/roundbox.py<br>
@@ -0,0 +1,100 @@<br>
+import math<br>
+import gtk<br>
+from sugar.graphics import style<br>
+<br>
+<br>
+class RoundBox(gtk.HBox):<br>
+ __gtype_name__ = 'RoundBox'<br>
+<br>
+ _BORDER_DEFAULT = style.LINE_WIDTH<br>
+<br>
+ def __init__(self, **kwargs):<br>
+ gtk.HBox.__init__(self, **kwargs)<br>
+<br>
+ self._x = None<br>
+ self._y = None<br>
+ self._width = None<br>
+ self._height = None<br>
+ self._radius = style.zoom(10)<br>
+ self.border = self._BORDER_DEFAULT<br>
+ self.border_color = style.COLOR_BLACK<br>
+ self.background_color = None<br>
+ self.set_reallocate_redraws(True)<br>
+ self.set_resize_mode(gtk.RESIZE_PARENT)<br>
+ self.connect("expose_event", self.__expose_cb)<br>
+ self.connect("add", self.__add_cb)<br>
+<br>
+ def __add_cb(self, child, params):<br>
+ child.set_border_width(style.zoom(5))<br>
+<br>
+ def __size_allocate_cb(self, widget, allocation):<br>
+ self._x = allocation.x<br>
+ self._y = allocation.y<br>
+ self._width = allocation.width<br>
+ self._height = allocation.height<br>
+<br>
+ def __expose_cb(self, widget, event):<br>
+ context = widget.window.cairo_create()<br>
+<br>
+ # set a clip region for the expose event<br>
+ context.rectangle(event.area.x, event.area.y,<br>
+ event.area.width, event.area.height)<br>
+ context.clip()<br>
+ self.draw(context)<br>
+ return False<br>
+<br>
+ def draw(self, cr):<br>
+ rect = self.get_allocation()<br>
+ x = rect.x + self._BORDER_DEFAULT / 2<br>
+ y = rect.y + self._BORDER_DEFAULT / 2<br>
+ width = rect.width - self._BORDER_DEFAULT<br>
+ height = rect.height - self._BORDER_DEFAULT<br>
+<br>
+ cr.move_to(x + self._radius, y)<br>
+ cr.arc(x + width - self._radius, y + self._radius,<br>
+ self._radius, math.pi * 1.5, math.pi * 2)<br>
+ cr.arc(x + width - self._radius, y + height - self._radius,<br>
+ self._radius, 0, math.pi * 0.5)<br>
+ cr.arc(x + self._radius, y + height - self._radius,<br>
+ self._radius, math.pi * 0.5, math.pi)<br>
+ cr.arc(x + self._radius, y + self._radius, self._radius,<br>
+ math.pi, math.pi * 1.5)<br>
+ cr.close_path()<br>
+<br>
+ if self.background_color is not None:<br>
+ r, g, b, __ = self.background_color.get_rgba()<br>
+ cr.set_source_rgb(r, g, b)<br>
+ cr.fill_preserve()<br>
+<br>
+ if self.border_color is not None:<br>
+ r, g, b, __ = self.border_color.get_rgba()<br>
+ cr.set_source_rgb(r, g, b)<br>
+ cr.set_line_width(self.border)<br>
+ cr.stroke()<br>
+<br>
+if __name__ == '__main__':<br>
+<br>
+ win = gtk.Window()<br>
+ win.connect('destroy', gtk.main_quit)<br>
+ win.set_default_size(450, 550)<br>
+ vbox = gtk.VBox()<br>
+<br>
+ box1 = RoundBox()<br>
+ vbox.add(box1)<br>
+ label1 = gtk.Label("Test 1")<br>
+ box1.add(label1)<br>
+<br>
+ rbox = RoundBox()<br>
+ rbox.background_color = style.Color('#FF0000')<br>
+ vbox.add(rbox)<br>
+ label2 = gtk.Label("Test 2")<br>
+ rbox.add(label2)<br>
+<br>
+ bbox = RoundBox()<br>
+ bbox.background_color = style.Color('#aaff33')<br>
+ bbox.border_color = style.Color('#ff3300')<br>
+ vbox.add(bbox)<br>
+<br>
+ win.add(vbox)<br>
+ win.show_all()<br>
+ gtk.main()<br>
<span class="HOEnZb"><font color="#888888">--<br>
1.7.10.4<br>
<br>
_______________________________________________<br>
Sugar-devel mailing list<br>
<a href="mailto:Sugar-devel@lists.sugarlabs.org">Sugar-devel@lists.sugarlabs.org</a><br>
<a href="http://lists.sugarlabs.org/listinfo/sugar-devel" target="_blank">http://lists.sugarlabs.org/listinfo/sugar-devel</a><br>
</font></span></blockquote></div><br><div>Daniel, your patch is in</div><div><br></div><div><a href="http://git.sugarlabs.org/speak/mainline/commit/a5ca5dca316a30bd01d9e97bdcee567aa3460671">http://git.sugarlabs.org/speak/mainline/commit/a5ca5dca316a30bd01d9e97bdcee567aa3460671</a></div>
<div><br></div><div>Y just had to make a little change.</div><div><br></div><div><a href="http://git.sugarlabs.org/speak/mainline/commit/27ee0e4bd2ef993d26e02427170910ea09917a4a">http://git.sugarlabs.org/speak/mainline/commit/27ee0e4bd2ef993d26e02427170910ea09917a4a</a></div>
<div><br></div><div>Thanks, this is a great improvement btw!.</div>