OSDN Git Service

calculate character width on chaging font, ThreadView
authorAiwota Programmer <aiwotaprog@tetteke.tk>
Tue, 25 Dec 2007 20:52:33 +0000 (05:52 +0900)
committerAiwota Programmer <aiwotaprog@tetteke.tk>
Tue, 25 Dec 2007 20:52:33 +0000 (05:52 +0900)
src/FukuiNoNamari/thread_view.py

index 0248e4a..6323435 100644 (file)
@@ -22,6 +22,13 @@ import pango
 from FukuiNoNamariExt import thread_view_extend
 
 
+def get_approximate_char_height(pango_context):
+    desc = pango_context.get_font_description()
+    font = pango_context.load_font(desc)
+    ink, log = font.get_glyph_extents(0)
+    return log[3] / pango.SCALE + 2
+
+
 class Line:
 
     HEIGHT = 15
@@ -41,9 +48,13 @@ class Line:
 
 class ElementEmpty:
 
-    def __init__(self):
+    def __init__(self, pango_layout):
+        self.pango_layout = pango_layout
         self.initialize()
 
+    def recalc_char_widths(self):
+        pass
+        
     def initialize(self):
         self.line_list = []
 
@@ -59,7 +70,9 @@ class ElementEmpty:
     def build_line_list(self, x, y, width, left_margin):
         self.initialize()
 
-        line = Line(0, 0, gtk.gdk.Rectangle(x, y, width - x, Line.HEIGHT))
+        line = Line(0, 0, gtk.gdk.Rectangle(
+            x, y, width - x,
+            get_approximate_char_height(self.pango_layout.get_context())))
         self.line_list.append(line)
 
         return width, y
@@ -76,6 +89,7 @@ class ElementText:
 
     def __init__(self, text, pango_layout):
         self.text = text
+        self.pango_layout = pango_layout
 
         attrlist = self._get_attrs()
         self.widths = thread_view_extend.get_char_width(
@@ -83,6 +97,11 @@ class ElementText:
 
         self.line_list = []
 
+    def recalc_char_widths(self):
+        attrlist = self._get_attrs()
+        self.widths = thread_view_extend.get_char_width(
+            self.pango_layout.get_context(), self.text, attrlist)
+
     def _get_attrs(self):
         attrs = pango.AttrList()
         return attrs
@@ -115,20 +134,21 @@ class ElementText:
         current_line_y = y
         current_line_width = 0
 
+        ch_h = get_approximate_char_height(self.pango_layout.get_context())
+
         for index, ch in enumerate(self.text):
             ch_w = self.widths[index]
-            ch_h = Line.HEIGHT
             if current_line_x + current_line_width + ch_w > width:
                 line = Line(
                     current_line_start_index, index,
                     gtk.gdk.Rectangle(
                     current_line_x, current_line_y,
-                    current_line_width, Line.HEIGHT))
+                    current_line_width, ch_h))
                 self.line_list.append(line)
 
                 current_line_start_index = index
                 current_line_x = left_margin
-                current_line_y += Line.HEIGHT
+                current_line_y += ch_h
                 current_line_width = ch_w
             else:
                 current_line_width += ch_w
@@ -138,7 +158,7 @@ class ElementText:
                         gtk.gdk.Rectangle(current_line_x,
                                           current_line_y,
                                           current_line_width,
-                                          Line.HEIGHT))
+                                          ch_h))
             self.line_list.append(line)
 
             current_line_x += current_line_width
@@ -235,7 +255,7 @@ class ResLayout:
 # represent one line
 
     def __init__(self, left_margin, resnum, pango_layout):
-        self.element_list = [ElementEmpty()]
+        self.element_list = [ElementEmpty(pango_layout)]
         self.width = 0
         self.height = 0
         self.pango_layout = pango_layout
@@ -271,6 +291,10 @@ class ResLayout:
             element = self.element_list[len(self.element_list) - 1]
         return element
 
+    def recalc_char_widths(self):
+        for element in self.element_list:
+            element.recalc_char_widths()
+
     def set_width(self, width):
 
         self.width = width
@@ -282,7 +306,7 @@ class ResLayout:
             current_x, current_y = element.build_line_list(
                 current_x, current_y, width, self.left_margin)
 
-        self.height = current_y + Line.HEIGHT
+        self.height = current_y + get_approximate_char_height(self.pango_layout.get_context())
 
     def get_pixel_size(self):
         return self.width, self.height
@@ -474,6 +498,8 @@ class ThreadView(gtk.HBox):
             "button-press-event", self.on_drawingarea_button_press_event)
         self.drawingarea.connect(
             "button-release-event", self.on_drawingarea_button_release_event)
+        self.drawingarea.connect(
+            "style-set", self.on_drawingarea_style_set)
         self.vscrollbar.connect(
             "value-changed", self.on_vscrollbar_value_changed)
 
@@ -535,6 +561,21 @@ class ThreadView(gtk.HBox):
     def redraw(self):
         self.drawingarea.queue_draw()
 
+    def wrap_relayout(self):
+        # before relayout, find top layout on gdkwindow
+        top_layout = self.get_layout_on_y(self.adjustment.value)
+        delta = 0
+
+        if top_layout is not None:
+            delta = top_layout.posY - self.vscrollbar.get_value()
+
+        self.relayout()
+        self.drawingarea_prev_width = self.drawingarea.allocation.width
+
+        # after relayout, set vscrollbar.value to top layout's posY
+        if top_layout is not None:
+            self.vscrollbar.set_value(top_layout.posY - delta)
+
     def relayout(self):
         self.layout_posY_map = [(0, [])]
 
@@ -756,24 +797,7 @@ class ThreadView(gtk.HBox):
 
     def on_drawingarea_configure_event(self, widget, event, data=None):
         if event.width != self.drawingarea_prev_width:
-
-            # before relayout, find top layout on gdkwindow
-            top_layout = None
-            delta = 0
-            for lay in self.res_layout_list:
-                if lay.posY > self.adjustment.value:
-                    break
-                top_layout = lay
-
-            if top_layout is not None:
-                delta = top_layout.posY - self.vscrollbar.get_value()
-
-            self.relayout()
-            self.drawingarea_prev_width = event.width
-
-            # after relayout, set vscrollbar.value to top layout's posY
-            if top_layout is not None:
-                self.vscrollbar.set_value(top_layout.posY - delta)
+            self.wrap_relayout()
 
         self.adjustment.page_size = self.drawingarea.allocation.height
         self.vscrollbar.set_increments(20, self.drawingarea.allocation.height)
@@ -880,3 +904,14 @@ class ThreadView(gtk.HBox):
                 if (uri == p_uri and layout == p_layout and
                     element == p_element):
                     self.on_uri_clicked(uri)
+
+    def on_drawingarea_style_set(self, widget, previous_style, data=None):
+        if previous_style is None:
+            return False
+
+        new = widget.style.font_desc.hash()
+        old = previous_style.font_desc.hash()
+        if new != old:
+            for layout in self.res_layout_list:
+                layout.recalc_char_widths()
+            self.wrap_relayout()