Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGonzalo Odiard <godiard@gmail.com>2013-06-11 15:24:45 (GMT)
committer Gonzalo Odiard <godiard@gmail.com>2013-06-11 22:18:33 (GMT)
commit6a04f9cc7427e6d3e204e0202418394aea9c8cd8 (patch)
tree7a2332d6a8f2ada6e46b05d54be5fe63fb213c15
parent75b84c723de83a823c18799d9c1becd9e1ca4b8a (diff)
Improve widgets to select font family and size with touch
Combos do not work well with touch, are replaced by new widgets provided in fontcombobox.py Signed-off-by: Gonzalo Odiard <gonzalo@laptop.org>
-rw-r--r--fontcombobox.py306
-rw-r--r--icons/font-text.svg32
-rw-r--r--icons/resize+.svg41
-rw-r--r--icons/resize-.svg43
-rw-r--r--toolbar.py23
-rw-r--r--widgets.py103
6 files changed, 409 insertions, 139 deletions
diff --git a/fontcombobox.py b/fontcombobox.py
index 9de5e34..0ae4d0f 100644
--- a/fontcombobox.py
+++ b/fontcombobox.py
@@ -15,52 +15,292 @@
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+import os
+import shutil
+from gettext import gettext as _
+
from gi.repository import Gtk
from gi.repository import GObject
+from gi.repository import Gio
+
+from sugar3.graphics.icon import Icon
+from sugar3.graphics.palette import Palette, ToolInvoker
+from sugar3.graphics.palettemenu import PaletteMenuBox
+from sugar3.graphics.palettemenu import PaletteMenuItem
+from sugar3.graphics import style
+from sugar3 import env
+
+DEFAULT_FONTS = ['Sans', 'Serif', 'Monospace']
+USER_FONTS_FILE_PATH = env.get_profile_path('fonts')
+GLOBAL_FONTS_FILE_PATH = '/etc/sugar_fonts'
+
+
+class FontLabel(Gtk.Label):
+
+ def __init__(self, default_font='Sans'):
+ Gtk.Label.__init__(self)
+ self._font = None
+ self.set_font(default_font)
+
+ def set_font(self, font):
+ if self._font != font:
+ self.set_markup('<span font="%s">%s</span>' % (font, font))
-FONT_BLACKLIST = ['cmex10', 'cmmi10', 'cmr10', 'cmsy10', 'esint10', 'eufm10',
- 'msam10', 'msbm10', 'rsfs10', 'wasy10']
+class FontComboBox(Gtk.ToolItem):
-class FontComboBox(Gtk.ComboBox):
+ __gsignals__ = {
+ 'changed': (GObject.SignalFlags.RUN_LAST, None, ([])), }
def __init__(self):
- GObject.GObject.__init__(self)
- font_renderer = Gtk.CellRendererText()
- self.pack_start(font_renderer, True)
- self.add_attribute(font_renderer, 'text', 0)
- self.add_attribute(font_renderer, 'font', 0)
- font_model = Gtk.ListStore(str)
+ self._palette_invoker = ToolInvoker()
+ Gtk.ToolItem.__init__(self)
+ self._font_label = FontLabel()
+ bt = Gtk.Button('')
+ bt.remove(bt.get_children()[0])
+ box = Gtk.HBox()
+ bt.add(box)
+ icon = Icon(icon_name='font-text')
+ box.pack_start(icon, False, False, 10)
+ box.pack_start(self._font_label, False, False, 10)
+ self.add(bt)
+ self.show_all()
+
+ self._font_name = 'Sans'
+
+ # theme the button, can be removed if add the style to the sugar css
+ if style.zoom(100) == 100:
+ subcell_size = 15
+ else:
+ subcell_size = 11
+ radius = 2 * subcell_size
+ theme = "GtkButton {border-radius: %dpx;}" % radius
+ css_provider = Gtk.CssProvider()
+ css_provider.load_from_data(theme)
+ style_context = bt.get_style_context()
+ style_context.add_provider(css_provider,
+ Gtk.STYLE_PROVIDER_PRIORITY_USER)
+
+ # init palette
+ self._hide_tooltip_on_click = True
+ self._palette_invoker.attach_tool(self)
+ self._palette_invoker.props.toggle_palette = True
+
+ self.palette = Palette(_('Select font'))
+ self.palette.set_invoker(self._palette_invoker)
+
+ # load the fonts in the palette menu
+ self._menu_box = PaletteMenuBox()
+ self.props.palette.set_content(self._menu_box)
+ self._menu_box.show()
context = self.get_pango_context()
- font_index = 0
- self.faces = {}
+ self._init_font_list()
+
+ tmp_list = []
for family in context.list_families():
name = family.get_name()
- if name not in FONT_BLACKLIST:
- font_model.append([name])
- # TODO gtk3
-# font_faces = []
-# for face in family.list_faces():
-# face_name = face.get_face_name()
-# font_faces.append(face_name)
-# self.faces[name] = font_faces
-
- font_model.set_sort_column_id(0, Gtk.SortType.ASCENDING)
- self.set_model(font_model)
- self.show()
+ if name in self._font_white_list:
+ tmp_list.append(name)
+ for name in sorted(tmp_list):
+ self._add_menu(name, self.__font_selected_cb)
+
+ self._font_label.set_font(self._font_name)
+
+ def _init_font_list(self):
+ self._font_white_list = []
+ self._font_white_list.extend(DEFAULT_FONTS)
+
+ # check if there are a user configuration file
+ if not os.path.exists(USER_FONTS_FILE_PATH):
+ # verify if exists a file in /etc
+ if os.path.exists(GLOBAL_FONTS_FILE_PATH):
+ shutil.copy(GLOBAL_FONTS_FILE_PATH, USER_FONTS_FILE_PATH)
+
+ if os.path.exists(USER_FONTS_FILE_PATH):
+ # get the font names in the file to the white list
+ fonts_file = open(USER_FONTS_FILE_PATH)
+ # get the font names in the file to the white list
+ for line in fonts_file:
+ self._font_white_list.append(line.strip())
+ # monitor changes in the file
+ gio_fonts_file = Gio.File.new_for_path(USER_FONTS_FILE_PATH)
+ self.monitor = gio_fonts_file.monitor_file(
+ Gio.FileMonitorFlags.NONE, None)
+ self.monitor.set_rate_limit(5000)
+ self.monitor.connect('changed', self._reload_fonts)
+
+ def _reload_fonts(self, monitor, gio_file, other_file, event):
+ if event != Gio.FileMonitorEvent.CHANGES_DONE_HINT:
+ return
+ self._font_white_list = []
+ self._font_white_list.extend(DEFAULT_FONTS)
+ fonts_file = open(USER_FONTS_FILE_PATH)
+ for line in fonts_file:
+ self._font_white_list.append(line.strip())
+ # update the menu
+ for child in self._menu_box.get_children():
+ self._menu_box.remove(child)
+ child = None
+ context = self.get_pango_context()
+ tmp_list = []
+ for family in context.list_families():
+ name = family.get_name()
+ if name in self._font_white_list:
+ tmp_list.append(name)
+ for name in sorted(tmp_list):
+ self._add_menu(name, self.__font_selected_cb)
+ return False
+
+ def __font_selected_cb(self, menu, font_name):
+ self._font_name = font_name
+ self._font_label.set_font(font_name)
+ self.emit('changed')
+
+ def _add_menu(self, font_name, activate_cb):
+ label = '<span font="%s">%s</span>' % (font_name, font_name)
+ menu_item = PaletteMenuItem()
+ menu_item.set_label(label)
+ menu_item.connect('activate', activate_cb, font_name)
+ self._menu_box.append_item(menu_item)
+ menu_item.show()
+
+ def __destroy_cb(self, icon):
+ if self._palette_invoker is not None:
+ self._palette_invoker.detach()
+
+ def create_palette(self):
+ return None
+
+ def get_palette(self):
+ return self._palette_invoker.palette
+
+ def set_palette(self, palette):
+ self._palette_invoker.palette = palette
+
+ palette = GObject.property(
+ type=object, setter=set_palette, getter=get_palette)
+
+ def get_palette_invoker(self):
+ return self._palette_invoker
+
+ def set_palette_invoker(self, palette_invoker):
+ self._palette_invoker.detach()
+ self._palette_invoker = palette_invoker
+
+ palette_invoker = GObject.property(
+ type=object, setter=set_palette_invoker, getter=get_palette_invoker)
def set_font_name(self, font_name):
- count = 0
- tree_iter = self.get_model().get_iter_first()
- while tree_iter is not None:
- value = self.get_model().get_value(tree_iter, 0)
- if value == font_name:
- self.set_active(count)
- count = count + 1
- tree_iter = self.get_model().iter_next(tree_iter)
+ self._font_label.set_font(font_name)
def get_font_name(self):
- tree_iter = self.get_active_iter()
- return self.get_model().get_value(tree_iter, 0)
+ return self._font_name
+
+
+class FontSize(Gtk.ToolItem):
+
+ __gsignals__ = {
+ 'changed': (GObject.SignalFlags.RUN_LAST, None, ([])), }
+
+ def __init__(self):
+
+ Gtk.ToolItem.__init__(self)
+
+ self._font_sizes = [8, 9, 10, 11, 12, 14, 16, 20, 22, 24, 26, 28, 36,
+ 48, 72]
+
+ # theme the buttons, can be removed if add the style to the sugar css
+ # these are the same values used in gtk-widgets.css.em
+ if style.zoom(100) == 100:
+ subcell_size = 15
+ default_padding = 6
+ else:
+ subcell_size = 11
+ default_padding = 4
+
+ hbox = Gtk.HBox()
+ vbox = Gtk.VBox()
+ self.add(vbox)
+ # add a vbox to set the padding up and down
+ vbox.pack_start(hbox, True, True, default_padding)
+ self._size_down = Gtk.Button()
+ icon = Icon(icon_name='resize-')
+ self._size_down.set_image(icon)
+ self._size_down.connect('clicked', self.__font_sizes_cb, False)
+ hbox.pack_start(self._size_down, False, False, 5)
+
+ # TODO: default?
+ self._default_size = 12
+ self._font_size = self._default_size
+
+ self._size_label = Gtk.Label(str(self._font_size))
+ hbox.pack_start(self._size_label, False, False, 10)
+
+ self._size_up = Gtk.Button()
+ icon = Icon(icon_name='resize+')
+ self._size_up.set_image(icon)
+ self._size_up.connect('clicked', self.__font_sizes_cb, True)
+ hbox.pack_start(self._size_up, False, False, 5)
+
+ radius = 2 * subcell_size
+ theme_up = "GtkButton {border-radius:0px %dpx %dpx 0px;}" % (radius,
+ radius)
+ css_provider_up = Gtk.CssProvider()
+ css_provider_up.load_from_data(theme_up)
+
+ style_context = self._size_up.get_style_context()
+ style_context.add_provider(css_provider_up,
+ Gtk.STYLE_PROVIDER_PRIORITY_USER)
+
+ theme_down = "GtkButton {border-radius: %dpx 0px 0px %dpx;}" % (radius,
+ radius)
+ css_provider_down = Gtk.CssProvider()
+ css_provider_down.load_from_data(theme_down)
+ style_context = self._size_down.get_style_context()
+ style_context.add_provider(css_provider_down,
+ Gtk.STYLE_PROVIDER_PRIORITY_USER)
+
+ self.show_all()
+
+ def __font_sizes_cb(self, button, increase):
+ if self._font_size in self._font_sizes:
+ i = self._font_sizes.index(self._font_size)
+ if increase:
+ if i < len(self._font_sizes) - 1:
+ i += 1
+ else:
+ if i > 0:
+ i -= 1
+ else:
+ i = self._font_sizes.index(self._default_size)
+
+ self._font_size = self._font_sizes[i]
+ self._size_label.set_text(str(self._font_size))
+ self._size_down.set_sensitive(i != 0)
+ self._size_up.set_sensitive(i < len(self._font_sizes) - 1)
+ self.emit('changed')
+
+ def set_font_size(self, size):
+ if size not in self._font_sizes:
+ # assure the font assigned is in the range
+ # if not, assign one close.
+ for font_size in self._font_sizes:
+ if font_size > size:
+ size = font_size
+ break
+ if size > self._font_sizes[-1]:
+ size = self._font_sizes[-1]
+
+ self._font_size = size
+ self._size_label.set_text(str(self._font_size))
+
+ # update the buttons states
+ i = self._font_sizes.index(self._font_size)
+ self._size_down.set_sensitive(i != 0)
+ self._size_up.set_sensitive(i < len(self._font_sizes) - 1)
+ self.emit('changed')
+
+ def get_font_size(self):
+ return self._font_size
diff --git a/icons/font-text.svg b/icons/font-text.svg
new file mode 100644
index 0000000..ad3f9fa
--- /dev/null
+++ b/icons/font-text.svg
@@ -0,0 +1,32 @@
+<?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="54.695999"
+ viewBox="0 0 55 54.696"
+ id="svg2"
+ xml:space="preserve"><metadata
+ id="metadata22"><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="defs20" /><text
+ x="-2.6176355"
+ y="43.461388"
+ transform="scale(0.90891499,1.1002129)"
+ id="text3591"
+ xml:space="preserve"
+ style="font-size:49.5765152px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Verdana;-inkscape-font-specification:Verdana"><tspan
+ x="-2.6176355"
+ y="43.461388"
+ id="tspan3593"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Serif;-inkscape-font-specification:Serif"><tspan
+ id="tspan2992"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Sans;-inkscape-font-specification:Sans">F</tspan>F</tspan></text>
+</svg> \ No newline at end of file
diff --git a/icons/resize+.svg b/icons/resize+.svg
new file mode 100644
index 0000000..0fae3c3
--- /dev/null
+++ b/icons/resize+.svg
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.1"
+ width="55"
+ height="54.695999"
+ viewBox="0 0 55 54.696"
+ id="svg2"
+ xml:space="preserve"><defs
+ id="defs21">
+
+
+
+
+ </defs><g
+ transform="translate(-0.4354,0)"
+ id="g7">
+ <g
+ id="g9">
+ <path
+ d="m 25.263,12.435 h 24.656 v 3.562 H 39.575 V 41.59 H 35.606 V 15.996 H 25.263 v -3.561 z"
+ id="path11"
+ style="fill:#ffffff" />
+ </g>
+ </g><g
+ transform="translate(-8.4356,0)"
+ id="g13">
+ <g
+ id="g15">
+ <path
+ d="m 13.953,24.435 h 16.656 v 3.562 H 24.265 V 41.59 H 20.296 V 27.997 h -6.344 v -3.562 z"
+ id="path17"
+ style="fill:#ffffff" />
+ </g>
+ </g><path
+ d="m 25.5774,23.677763 6,-6.000001 -4,0"
+ id="path3618"
+ style="fill:none;stroke:#ffffff;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></svg> \ No newline at end of file
diff --git a/icons/resize-.svg b/icons/resize-.svg
new file mode 100644
index 0000000..e3b719e
--- /dev/null
+++ b/icons/resize-.svg
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.1"
+ width="55"
+ height="54.695999"
+ viewBox="0 0 55 54.696"
+ id="svg2"
+ xml:space="preserve"><defs
+ id="defs21">
+
+
+
+
+ </defs><g
+ transform="translate(3.5644,0)"
+ id="g2821"><g
+ transform="matrix(-1,0,0,1,51.871,0)"
+ id="g7">
+ <g
+ id="g9">
+ <path
+ d="m 25.263,12.435 h 24.656 v 3.562 H 39.575 V 41.59 H 35.606 V 15.996 H 25.263 v -3.561 z"
+ id="path11"
+ style="fill:#ffffff" />
+ </g>
+ </g><g
+ transform="matrix(-1,0,0,1,59.8712,0)"
+ id="g13">
+ <g
+ id="g15">
+ <path
+ d="m 13.953,24.435 h 16.656 v 3.562 H 24.265 V 41.59 H 20.296 V 27.997 h -6.344 v -3.562 z"
+ id="path17"
+ style="fill:#ffffff" />
+ </g>
+ </g><path
+ d="m 19.8582,17.677762 6,6.000001 -4,0"
+ id="path3618"
+ style="fill:none;stroke:#ffffff;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g></svg> \ No newline at end of file
diff --git a/toolbar.py b/toolbar.py
index e344776..3b18f13 100644
--- a/toolbar.py
+++ b/toolbar.py
@@ -41,8 +41,8 @@ from sugar3.activity.widgets import RedoButton
from widgets import AbiButton
from widgets import AbiMenuItem
-from widgets import FontSizeCombo
from fontcombobox import FontComboBox
+from fontcombobox import FontSize
from gridcreate import GridCreateWidget
logger = logging.getLogger('write-activity')
@@ -430,8 +430,12 @@ class TextToolbar(Gtk.Toolbar):
self._font_family_cb)
self.insert(ToolComboBox(self.font_name_combo), -1)
- font_size = ToolComboBox(FontSizeCombo(abiword_canvas))
- self.insert(font_size, -1)
+ self.font_size = FontSize()
+ self._abi_handler = abiword_canvas.connect('font-size',
+ self._font_size_cb)
+ self._changed_id = self.font_size.connect(
+ 'changed', self._font_size_changed_cb, abiword_canvas)
+ self.insert(self.font_size, -1)
bold = ToggleToolButton('format-text-bold')
bold.set_tooltip(_('Bold'))
@@ -520,6 +524,19 @@ class TextToolbar(Gtk.Toolbar):
logging.debug('Abiword font changed to %s', font_family)
self.font_name_combo.set_font_name(font_family)
+ def _font_size_changed_cb(self, widget, abi):
+ abi.handler_block(self._abi_handler)
+ try:
+ abi.set_font_size(str(widget.get_font_size()))
+ finally:
+ abi.handler_unblock(self._abi_handler)
+
+ def _font_size_cb(self, abi, size):
+ logging.debug('Abiword font size changed to %s', size)
+ self.handler_block(self._changed_id)
+ self.font_size.set_font_size(int(size))
+ self.handler_unblock(self._changed_id)
+
def _setToggleButtonState(self, button, b, id):
button.handler_block(id)
button.set_active(b)
diff --git a/widgets.py b/widgets.py
index 18aa08a..2579007 100644
--- a/widgets.py
+++ b/widgets.py
@@ -22,8 +22,6 @@ from gi.repository import Abi
from gi.repository import GLib
from sugar3.graphics.radiotoolbutton import RadioToolButton
-from sugar3.graphics.combobox import ComboBox
-from sugar3.graphics.palette import Palette
from sugar3.graphics.toolbutton import ToolButton
from sugar3.graphics.palettemenu import PaletteMenuItem
from sugar3.datastore import datastore
@@ -32,107 +30,6 @@ from sugar3.activity.activity import SCOPE_PRIVATE
logger = logging.getLogger('write-activity')
-"""
-# The FontCombo is not used anymore, keep the code here for reference
-# for a few versions
-
-class FontCombo(ComboBox):
-
- def __init__(self, abi):
- ComboBox.__init__(self)
-
- self._has_custom_fonts = False
- self._fonts = sorted(abi.get_font_names())
- self._fonts_changed_id = self.connect('changed', self._font_changed_cb,
- abi)
-
- for i, f in enumerate(self._fonts):
- self.append_item(i, f, None)
- if f == 'Times New Roman':
- self.set_active(i)
-
- self._abi_handler = abi.connect('font-family', self._font_family_cb)
-
- def _font_changed_cb(self, combobox, abi):
- if self.get_active() != -1:
- logger.debug('Setting font: %s', self._fonts[self.get_active()])
- try:
- abi.handler_block(self._abi_handler)
- abi.set_font_name(self._fonts[self.get_active()])
- finally:
- abi.handler_unblock(self._abi_handler)
-
- def _font_family_cb(self, abi, font_family):
- font_index = -1
-
- # search for the font name in our font list
- for i, f in enumerate(self._fonts):
- if f == font_family:
- font_index = i
- break
-
- # if we don't know this font yet, then add it (temporary) to the list
- if font_index == -1:
- logger.debug('Font not found in font list: %s', font_family)
- if not self._has_custom_fonts:
- # add a separator to seperate the non-available fonts from
- # the available ones
- self._fonts.append('') # ugly
- self.append_separator()
- self._has_custom_fonts = True
- # add the new font
- self._fonts.append(font_family)
- self.append_item(0, font_family, None)
- # see how many fonts we have now, so we can select the last one
- model = self.get_model()
- num_children = model.iter_n_children(None)
- logger.debug('Number of fonts in the list: %d', num_children)
- font_index = num_children - 1
-
- # activate the found font
- if (font_index > -1):
- self.handler_block(self._fonts_changed_id)
- self.set_active(font_index)
- self.handler_unblock(self._fonts_changed_id)
-"""
-
-
-class FontSizeCombo(ComboBox):
-
- def __init__(self, abi):
- ComboBox.__init__(self)
-
- self._abi_handler = abi.connect('font-size', self._font_size_cb)
-
- self._font_sizes = ['8', '9', '10', '11', '12', '14', '16', '20', \
- '22', '24', '26', '28', '36', '48', '72']
- self._changed_id = self.connect('changed', self._font_size_changed_cb,
- abi)
-
- for i, s in enumerate(self._font_sizes):
- self.append_item(i, s, None)
- if s == '12':
- self.set_active(i)
-
- def _font_size_changed_cb(self, combobox, abi):
- if self.get_active() != -1:
- logger.debug('Setting font size: %d',
- int(self._font_sizes[self.get_active()]))
-
- abi.handler_block(self._abi_handler)
- try:
- abi.set_font_size(self._font_sizes[self.get_active()])
- finally:
- abi.handler_unblock(self._abi_handler)
-
- def _font_size_cb(self, abi, size):
- for i, s in enumerate(self._font_sizes):
- if int(s) == int(size):
- self.handler_block(self._changed_id)
- self.set_active(i)
- self.handler_unblock(self._changed_id)
- break
-
class AbiButton(RadioToolButton):