示例#1
0
 def _configure_cb(self, widget, event):
     """Internal: Update size and prefs when window is adjusted"""
     # Constrain window to fit on its current monitor, if possible.
     screen = event.get_screen()
     mon = screen.get_monitor_at_point(event.x, event.y)
     mon_geom = screen.get_monitor_geometry(mon)
     # Constrain width and height
     w = clamp(int(event.width), self.MIN_WIDTH, self.MAX_WIDTH)
     h = clamp(int(event.height), self.MIN_HEIGHT, self.MAX_HEIGHT)
     # Constrain position
     x, y = event.x, event.y
     if y + h > mon_geom.y + mon_geom.height:
         y = mon_geom.y + mon_geom.height - h
     if x + w > mon_geom.x + mon_geom.width:
         x = mon_geom.x + mon_geom.width - w
     if x < mon_geom.x:
         x = mon_geom.x
     if y < mon_geom.y:
         y = mon_geom.y
     event_size = (event.x, event.y, event.width, event.height)
     ex, ey, ew, eh = [int(c) for c in event_size]
     x, y, w, h = [int(c) for c in (x, y, w, h)]
     if not self._corrected_pos:
         if (x, y) != (ex, ey):
             GLib.idle_add(self.move, x, y)
         if (w, h) != (ew, eh):
             GLib.idle_add(self.resize, w, h)
         self._corrected_pos = True
     # Record size
     self._size = (x, y, w, h)
     self.app.preferences[self._prefs_size_key] = (w, h)
示例#2
0
 def _complete_initial_layout(self):
     """Finish initial layout; called after toplevel win is positioned"""
     # Init tool group sizes by setting vpaned positions
     for paned in self._get_paneds():
         if paned._initial_divider_position:
             pos = paned._initial_divider_position
             GLib.idle_add(paned.set_position, pos)
示例#3
0
 def _delete_cb(self, widget):
     """Properties dialog delete callback"""
     self._dialog.hide()
     name = brushmanager.translate_group_name(self._group)
     msg = C_(
         "brush group delete",
         u"Really delete group \u201C{group_name}\u201D?",
     ).format(group_name=name, )
     bm = self._app.brushmanager
     if not dialogs.confirm(self, msg):
         return
     bm.delete_group(self._group)
     if self._group not in bm.groups:
         GLib.idle_add(
             self._remove_panel_idle_cb,
             self.__gtype_name__,
             (self._group, ),
         )
         return
     # Special groups like "Deleted" cannot be deleted,
     # but the error message is very confusing in that case...
     msg = C_(
         "brush group delete",
         u"Could not delete group \u201C{group_name}\u201D.\n"
         u"Some special groups cannot be deleted.",
     ).format(group_name=name, )
     dialogs.error(self, msg)
    def _build_ui(self):
        """Builds the UI from ``brusheditor.glade``"""
        ui_dir = os.path.dirname(os.path.abspath(__file__))
        ui_path = os.path.join(ui_dir, self._UI_DEFINITION_FILE)
        with open(ui_path, 'r') as ui_fp:
            ui_xml = ui_fp.read()
        self._builder.add_from_string(ui_xml)
        self._populate_inputs(ui_xml)
        self._populate_settings_treestore()
        self._builder.connect_signals(self)
        for inp in brushsettings.inputs:
            grid = self._builder.get_object("by%s_curve_grid" % inp.name)
            GLib.idle_add(grid.hide)
            curve = self._builder.get_object("by%s_curve" % inp.name)

            def _curve_changed_cb(curve, i=inp):
                self._update_brush_from_input_widgets(i)

            curve.changed_cb = _curve_changed_cb
            btn = self._builder.get_object("by%s_reset_button" % inp.name)
            btn.connect("clicked", self.input_adj_reset_button_clicked_cb, inp)
        # Certain actions must be coordinated via a real app instance
        if not self.app:
            action_buttons = [
                "clone_button", "rename_button", "edit_icon_button",
                "delete_button", "live_update_checkbutton", "save_button"
            ]
            for b_name in action_buttons:
                w = self._builder.get_object(b_name)
                w.set_sensitive(False)
示例#5
0
    def activate_from_button_event(self, event):
        """Activate during handling of a GdkEventButton (press/release)

        If the event is a button press, then the grab will start
        immediately, begin updating immediately, and will terminate by
        the release of the initiating button.

        If the event is a button release, then the grab start will be
        deferred to start in an idle handler. When the grab starts, it
        won't begin updating until the user clicks button 1 (and only
        button 1), and it will only be terminated with a button1
        release. This covers the case of events delivered to "clicked"
        signal handlers

        """
        if event.type == Gdk.EventType.BUTTON_PRESS:
            logger.debug("Starting picking grab")
            has_button_info, button_num = event.get_button()
            if not has_button_info:
                return
            self._start_grab(event.device, event.time, button_num)
        elif event.type == Gdk.EventType.BUTTON_RELEASE:
            logger.debug("Queueing picking grab")
            GLib.idle_add(
                self._start_grab,
                event.device,
                event.time,
                None,
            )
示例#6
0
    def target(self, targ):
        inkmode, cn_idx = targ
        inkmode_ref = None
        if inkmode:
            inkmode_ref = weakref.ref(inkmode)
        self._target = (inkmode_ref, cn_idx)

        GLib.idle_add(self._update_ui_for_current_target)
示例#7
0
    def _post_show_cb(self, widget):
        # Ensure the tree selection matches the root stack's current layer.
        self._update_selection()

        # Match the flag column widths to the name column's height.
        # This only makes sense after the 1st text layout, sadly.
        GLib.idle_add(self._sizeify_flag_columns)

        return False
示例#8
0
        def _sidebar_swap_button_clicked_cb(self, button):
            """Switch the current page's sidebar ("clicked" event handler)

            Ultimately fires the tool_widget_removed() and
            tool_widget_added() @events of the owning workspace.

            """
            page_num = self.get_current_page()
            page = self.get_nth_page(page_num)
            if page is not None:
                GLib.idle_add(self._deferred_swap_tool_widget_sidebar, page)
示例#9
0
        def _close_button_clicked_cb(self, button):
            """Remove the current page (close button "clicked" event callback)

            Ultimately fires the ``tool_widget_removed()`` @event of the owning
            workspace.

            """
            page_num = self.get_current_page()
            page = self.get_nth_page(page_num)
            if page is not None:
                GLib.idle_add(self._deferred_remove_tool_widget, page)
 def _set_input_expanded(self, inp, expand, scroll=True):
     getobj = self._builder.get_object
     arrow = getobj("by%s_expander_arrow" % (inp.name, ))
     grid = getobj("by%s_curve_grid" % (inp.name, ))
     if expand:
         arrow.set_property("arrow-type", Gtk.ArrowType.DOWN)
         grid.show_all()
         if scroll:
             GLib.idle_add(self._scroll_setting_editor, grid)
     else:
         arrow.set_property("arrow-type", Gtk.ArrowType.RIGHT)
         grid.hide()
示例#11
0
    def add_tool_widget(self, widget, maxnotebooks=None, maxpages=3):
        """Tries to find space for, then add and show a tool widget

        Finding space is based on constraints, adjustable via the parameters.
        The process is driven by `Workspace.add_tool_widget()`.

        :param widget: the widget that needs adoption.
        :type widget: Gtk.Widget created by the Workspace's factory.
        :param maxnotebooks: never make more than this many groups in the stack
        :type maxnotebooks: int
        :param maxpages: never make more than this many pages in a group
        :type maxpages: int
        :return: whether space was found for the widget
        :rtype: bool

        The idea is to try repeatedly with gradually relaxing constraint
        parameters across all stacks in the system until space is found
        somewhere.

        """
        # Try to find a notebook with few enough pages.
        # It might be the zero-pages placeholder on the end.
        target_notebook = None
        notebooks = self._get_notebooks()
        assert len(notebooks) > 0, (
            "There should always be at least one Notebook widget "
            "in any ToolStack.")
        for nb in notebooks:
            if nb.get_n_pages() < maxpages:
                target_notebook = nb
                break
        # The placeholder tends to be recreated in idle routines,
        # so it may not be present on the end of the stack just yet.
        if target_notebook is None:
            assert nb.get_n_pages() > 0
            new_placeholder = nb.split_former_placeholder()
            target_notebook = new_placeholder
        # Adding a page to the placeholder would result in a split
        # in the idle routine later. Check constraint now.
        if maxnotebooks and (target_notebook.get_n_pages() == 0):
            n_populated_notebooks = len(
                [n for n in notebooks if n.get_n_pages() > 0])
            if n_populated_notebooks >= maxnotebooks:
                return False
        # We're good to go.
        target_notebook.append_tool_widget_page(widget)
        if self.workspace:
            GLib.idle_add(self.workspace.tool_widget_added, widget)
        return True
示例#12
0
 def _values_changed_idle_cb(self):
     # Aggregate, idle-state callback for multiple adjustments being changed
     # in a single event. Queues redraws, and runs observers. The curve sets
     # multiple settings at once, and we might as well not queue too many
     # redraws.
     if self._idle_srcid is not None:
         current_mode = self.app.doc.modes.top
         if isinstance(current_mode, LineModeBase):
             # Redraw last_line when settings are adjusted
             # in the adjustment Curve
             GLib.idle_add(current_mode.redraw_line_cb)
         for func in self.observers:
             func(self._changed_settings)
         self._changed_settings = set()
         self._idle_srcid = None
     return False
示例#13
0
 def drag_update_cb(self, tdw, event, ev_x, ev_y, dx, dy):
     if self._line_possible:
         self.update_position(ev_x, ev_y)
         if self.idle_srcid is None:
             self.idle_srcid = GLib.idle_add(self._drag_idle_cb)
     return super(LineModeBase,
                  self).drag_update_cb(tdw, event, ev_x, ev_y, dx, dy)
示例#14
0
 def _value_changed_cb(self, adj, prefs_key):
     # Direct GtkAdjustment callback for a single adjustment being changed.
     value = float(adj.get_value())
     self.app.preferences[prefs_key] = value
     self._changed_settings.add(prefs_key)
     if self._idle_srcid is None:
         self._idle_srcid = GLib.idle_add(self._values_changed_idle_cb)
示例#15
0
    def _realize_cb(self, widget):
        """Kick off the deferred layout code when the widget is realized"""

        # Set up monitoring of the toplevel's size changes.
        toplevel = self.get_toplevel()
        toplevel.connect("configure-event", self._toplevel_configure_cb)

        # Do the initial layout
        layout = self._initial_layout
        if layout is None:
            return
        llayout = layout.get("left_sidebar", {})
        if llayout:
            logger.debug("Left sidebar: building from saved layout...")
            num_added_l = self._lstack.build_from_layout(llayout)
            logger.debug("Left sidebar: added %d group(s)", num_added_l)
        rlayout = layout.get("right_sidebar", {})
        if rlayout:
            logger.debug("Right sidebar: building from saved layout...")
            num_added_r = self._rstack.build_from_layout(rlayout)
            logger.debug("Right sidebar: added %d group(s)", num_added_r)
        # Floating windows
        for fi, flayout in enumerate(layout.get("floating", [])):
            logger.debug(
                "Floating window %d: building from saved layout...",
                fi,
            )
            win = ToolStackWindow(self)
            self.floating_window_created(win)
            num_added_f = win.build_from_layout(flayout)
            logger.debug(
                "Floating window %d: added %d group(s)",
                fi,
                num_added_f,
            )
            # The populated ones are only revealed after their
            # floating_window_created have had a chance to run.
            if num_added_f > 0:
                self._floating.add(win)
                GLib.idle_add(win.show_all)
            else:
                logger.warning(
                    "Floating window %d is initially unpopulated. "
                    "Destroying it.",
                    fi,
                )
                GLib.idle_add(win.destroy)
示例#16
0
        def _first_alloc_cb(self, widget, alloc):
            """Try to allocate child widgets their natural size when alloced.
            """
            # Normally, if child widgets declare a real minimum size then in a
            # structure like this they'll be allocated their minimum size even
            # when there's enough space to give them their natural size. As a
            # workaround, set the bar position on the very first size-allocate
            # event to the best compromise we can calculate.

            # Child natural and minimum heights.
            c1 = self.get_child1()
            c2 = self.get_child2()
            if not (c1 and c2):
                return
            c1min, c1nat = c1.get_preferred_height_for_width(alloc.width)
            c2min, c2nat = c2.get_preferred_height_for_width(alloc.width)

            # Disconnect the handler; only run the 1st time.
            self.disconnect(self._first_alloc_id)
            self._first_alloc_id = None

            # If ToolStack.build_from_layout set an initial position,
            # then code elsewhere will be allocating a size.
            if self._initial_divider_position is not None:
                return

            # Get handle size
            handle_size = GObject.Value()
            handle_size.init(int)
            handle_size.set_int(12)  # conservative initial guess
            self.style_get_property("handle-size", handle_size)
            bar_height = handle_size.get_int()
            # Strategy here is to try and give one child widget its natural
            # size first, slightly favouring the first (top) child.  We
            # could be more egalitarian by inspecting the deep structure.
            pos = -1
            if c1nat + c2min <= alloc.height - bar_height:
                pos = c1nat
            elif c1min + c2nat <= alloc.height - bar_height:
                pos = alloc.height - c2nat - bar_height
            elif c1min + c2min <= alloc.height - bar_height:
                pos = alloc.height - c2min - bar_height

            # The position setting must be done outside this handler
            # or it'll look weird.
            GLib.idle_add(self.set_position, pos)
示例#17
0
 def _page_added_cb(self, notebook, child, page_num):
     stack = self._toolstack
     GLib.idle_add(stack._update_structure)
     # Reinstate the previous size on the divider
     # if this is the result of dragging a tab out
     # into a new window.
     try:
         size = child.__prev_size
     except AttributeError:
         return
     if self is not stack._get_first_notebook():
         return
     if self.get_n_pages() != 1:
         return
     # The size-setting must be done outside the event handler
     # for it to take effect.
     w, h = size
     GLib.idle_add(stack._set_first_paned_position, h)
示例#18
0
    def build_from_layout(self, layout):
        """Builds the workspace from a definition dict.

        :param layout: a layout definition
        :type layout: dict

        In order to have any effect, this must be called before the workspace
        widget is realized, but after it has been packed into its toplevel
        window. Keys and values in the dict are as follows:

        * position: an initial window position dict for the toplevel
          window. See `set_initial_window_position()`.
        * left_sidebar, right_sidebar: `ToolStack` definition lists.
          See `Toolstack.build_from_layout()`.
        * floating: a list of floating window definitions. Each element
          is a dict with the following keys:
          - contents: a `ToolStack` definition dict: see above.
          - position: an initial window position dict: see above.
        * autohide: whether autohide is enabled when fullscreening.
        * fullsceen: whether to start in fullscreen mode.
        * maximized: whether to start maximized.

        See also `get_layout()`.

        """
        toplevel_win = self.get_toplevel()
        assert toplevel_win is not None
        assert toplevel_win is not self
        assert not toplevel_win.get_visible()
        # Set initial position and fullscreen state
        toplevel_pos = layout.get("position", None)
        if toplevel_pos:
            set_initial_window_position(toplevel_win, toplevel_pos)
        if layout.get("fullscreen", False):
            toplevel_win.fullscreen()
            GLib.idle_add(lambda *a: toplevel_win.fullscreen())
        elif layout.get("maximized", False):
            toplevel_win.maximize()
            GLib.idle_add(lambda *a: toplevel_win.maximize())
        toplevel_win.connect("window-state-event",
                             self._toplevel_window_state_event_cb)
        self.autohide_enabled = layout.get("autohide", True)
        self._initial_layout = layout
示例#19
0
 def drag_update_cb(self, tdw, event, dx, dy):
     """UI and model updates during a drag"""
     if self._cmd:
         assert tdw is self._drag_active_tdw
         x, y = tdw.display_to_model(event.x, event.y)
         self._cmd.move_to(x, y)
         if self._drag_update_idler_srcid is None:
             idler = self._drag_update_idler
             self._drag_update_idler_srcid = GLib.idle_add(idler)
     return super(LayerMoveMode, self).drag_update_cb(tdw, event, dx, dy)
示例#20
0
    def update(self, width=None, height=None):
        """
        Redraws the widget from scratch.
        """
        self.total_border = self.border_visible \
            + self.spacing_inside + self.spacing_outside
        self.total_w = self.item_w + 2 * self.total_border
        self.total_h = self.item_h + 2 * self.total_border

        if width is None:
            if not self.pixbuf:
                return
            width = self.pixbuf.get_width()
            height = self.pixbuf.get_height()
        width = max(width, self.total_w)
        self.tiles_w = max(1, int(width // self.total_w))
        self.tiles_h = max(1, int(ceil(len(self.itemlist) / self.tiles_w)))

        height = self.tiles_h * self.total_h
        # self.set_size_request(-1, -1)
        GLib.idle_add(self.set_size_request, self.total_w, height)

        self.pixbuf = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, True, 8,
                                           width, height)
        self.pixbuf.fill(0xffffff00)  # transparent
        for i, item in enumerate(self.itemlist):
            x = (i % self.tiles_w) * self.total_w
            y = (i // self.tiles_w) * self.total_h
            x += self.total_border
            y += self.total_border

            pixbuf = self.pixbuffunc(item)
            if pixbuf not in self.thumbnails:
                self.thumbnails[pixbuf] = helpers.pixbuf_thumbnail(
                    pixbuf,
                    self.item_w,
                    self.item_h,
                )
            pixbuf = self.thumbnails[pixbuf]
            pixbuf.composite(self.pixbuf, x, y, self.item_w, self.item_h, x, y,
                             1, 1, GdkPixbuf.InterpType.BILINEAR, 255)

        self.queue_draw()
示例#21
0
    def drag_stop_cb(self, tdw):
        """UI and model updates at the end of a drag"""
        # Stop the update idler running on its next scheduling
        self._drag_update_idler_srcid = None
        # This will leave a non-cleaned-up move if one is still active,
        # so finalize it in its own idle routine.
        if self._cmd is not None:
            assert tdw is self._drag_active_tdw
            # Arrange for the background work to be done, and look busy
            tdw.set_sensitive(False)

            window = tdw.get_window()
            cursor = Gdk.Cursor.new_for_display(window.get_display(),
                                                Gdk.CursorType.WATCH)
            tdw.set_override_cursor(cursor)

            self.final_modifiers = self.current_modifiers()
            GLib.idle_add(self._finalize_move_idler)
        else:
            # Still need cleanup for tracking state, cursors etc.
            self._drag_cleanup()
        return super(LayerMoveMode, self).drag_stop_cb(tdw)
示例#22
0
 def _map_cb(self, widget):
     """Window map event actions"""
     toplevel = None
     workspace = self.stack.workspace
     if workspace:
         toplevel = workspace.get_toplevel()
     # Things we only need to do on the first window map
     if not self._mapped_once:
         self._mapped_once = True
         if toplevel:
             self.set_transient_for(toplevel)
         if workspace:
             workspace._floating.add(self)
         win = widget.get_window()
         decor = (Gdk.WMDecoration.TITLE
                  | Gdk.WMDecoration.BORDER
                  | Gdk.WMDecoration.RESIZEH)
         win.set_decorations(decor)
         wmfuncs = Gdk.WMFunction.RESIZE | Gdk.WMFunction.MOVE
         win.set_functions(wmfuncs)
     # Hack to force an initial x,y position to be what was saved, used
     # as a workaround for WM bugs and misfeatures.
     # Forcing the position up front rather than in an idle handler
     # avoids flickering in Xfce 4.8, when this is necessary.
     # Xfce 4.8 requires position forcing for second and subsequent
     # map events too, if a window has been resized due its content growing.
     # Hopefully we never have to do this twice. Once is too much really.
     if self._onmap_position is not None:
         if self._AGGRESSIVE_POSITIONING_HACK:
             self._set_onmap_position(False)
             GLib.idle_add(self._set_onmap_position, True)
         else:
             self._set_onmap_position(True)
     # Prevent subwindows from taking keyboard focus from the main window
     # in Metacity by presenting it again. https://gna.org/bugs/?17899
     # Still affects GNOME 3.14.
     # https://github.com/mypaint/mypaint/issues/247
     if toplevel:
         GLib.idle_add(lambda *a: toplevel.present())
 def _queue_live_update(self):
     """Queues a single live update of the most recent brushstroke"""
     # Not if already queued, or if disabled
     if self._live_update_idle_cb_id:
         return
     if not self._live_update_enabled:
         return
     # Only in live-updatable modes.
     # Currently: only FreehandMode supports this. It could potentially
     # work with other modes: please test!
     if not getattr(self.app.doc.modes.top, "IS_LIVE_UPDATEABLE", False):
         return
     cbid = GLib.idle_add(self._live_update_idle_cb)
     self._live_update_idle_cb_id = cbid
示例#24
0
    def add_work(self, func, *args, **kwargs):
        """Adds work

        :param func: a task callable.
        :param *args: passed to func
        :param **kwargs: passed to func

        This starts the queue running if it isn't already.
        Each callable will be called with the given parameters
        until it returns false, at which point it's discarded.

        """
        if not self._idle_id:
            self._idle_id = GLib.idle_add(
                self._process,
                priority=self._priority,
            )
        self._queue.append((func, args, kwargs))
示例#25
0
 def _in_grab_motion_cb(self, widget, event):
     assert self._grabbed_pointer_dev is not None
     if not self._check_event_devices_still_grabbed(event):
         return True
     if event.device is not self._grabbed_pointer_dev:
         return False
     if not self._grab_button_num:
         return False
     # Due to a performance issue, picking can take more time
     # than we have between two motion events (about 8ms).
     if self._delayed_picking_update_id:
         GLib.source_remove(self._delayed_picking_update_id)
     self._delayed_picking_update_id = GLib.idle_add(
         self._delayed_picking_update_cb,
         event.device,
         event.x_root,
         event.y_root,
     )
     return True
示例#26
0
def composite(handler, fill_args, trim_result, filled, tiles_bbox, dst):
    """Composite the filled tiles into the destination surface"""

    handler.set_stage(handler.COMPOSITE, len(filled))

    fill_col = fill_args.color

    # Prepare opaque color rgba tile for copying
    full_rgba = myplib.rgba_tile_from_alpha_tile(
        _FULL_TILE, *(fill_col + (0, 0, N - 1, N - 1)))

    # Bounding box of tiles that need updating
    dst_changed_bbox = None
    dst_tiles = dst.get_tiles()

    skip_empty_dst = fill_args.skip_empty_dst()
    mode = fill_args.mode
    lock_alpha = fill_args.lock_alpha
    opacity = fill_args.opacity

    tile_combine = myplib.tile_combine

    # Composite filled tiles into the destination surface
    for tile_coord, src_tile in iteritems(filled):

        if not handler.run:
            break

        handler.inc_processed()

        # Omit tiles outside of the bounding box _if_ the frame is enabled
        # Note:filled tiles outside bbox only originates from dilation/blur
        if trim_result and tiles_bbox.outside(tile_coord):
            continue

        # Skip empty destination tiles for erasing and alpha locking
        # Avoids completely unnecessary tile allocation and copying
        if skip_empty_dst and tile_coord not in dst_tiles:
            continue

        with dst.tile_request(*tile_coord, readonly=False) as dst_tile:

            # Only at this point might the bounding box need to be updated
            dst_changed_bbox = update_bbox(dst_changed_bbox, *tile_coord)

            # Under certain conditions, direct copies and dict manipulation
            # can be used instead of compositing operations.
            cut_off = trim_result and tiles_bbox.crossing(tile_coord)
            full_inner = src_tile is _FULL_TILE and not cut_off
            if full_inner:
                if mode == myplib.CombineNormal and opacity == 1.0:
                    myplib.tile_copy_rgba16_into_rgba16(full_rgba, dst_tile)
                    continue
                elif mode == myplib.CombineDestinationOut and opacity == 1.0:
                    dst_tiles.pop(tile_coord)
                    continue
                elif mode == myplib.CombineDestinationIn and opacity == 1.0:
                    continue
                # Even if opacity != 1.0, we can reuse the full rgba tile
                src_tile_rgba = full_rgba
            else:
                if trim_result:
                    tile_bounds = tiles_bbox.tile_bounds(tile_coord)
                else:
                    tile_bounds = (0, 0, N - 1, N - 1)
                src_tile_rgba = myplib.rgba_tile_from_alpha_tile(
                    src_tile, *(fill_col + tile_bounds))

            # If alpha locking is enabled in combination with a mode other than
            # CombineNormal, we need to copy the dst tile to mask the result
            if lock_alpha and mode != myplib.CombineSourceAtop:
                mask = np.copy(dst_tile)
                mask_mode = myplib.CombineDestinationAtop
                tile_combine(mode, src_tile_rgba, dst_tile, True, opacity)
                tile_combine(mask_mode, mask, dst_tile, True, 1.0)
            else:
                tile_combine(mode, src_tile_rgba, dst_tile, True, opacity)

    # Handle dst-out and dst-atop: clear untouched tiles
    if mode in [myplib.CombineDestinationIn, myplib.CombineDestinationAtop]:
        for tile_coord in list(dst_tiles.keys()):
            if not handler.run:
                break
            if tile_coord not in filled:
                dst_changed_bbox = update_bbox(dst_changed_bbox, *tile_coord)
                with dst.tile_request(*tile_coord, readonly=False):
                    dst_tiles.pop(tile_coord)

    if dst_changed_bbox and handler.run:
        min_tx, min_ty, max_tx, max_ty = dst_changed_bbox
        bbox = (
            min_tx * N,
            min_ty * N,
            (1 + max_tx - min_tx) * N,
            (1 + max_ty - min_ty) * N,
        )
        # Even for large fills on slow machines, this stage
        # will almost always be too short to even notice.
        # It is not cancellable once entered.
        handler.set_stage(FillHandler.FINISHING)

        # The observers may directly or indirectly use the
        # Gtk API, so the call is scheduled on the gui thread.
        GLib.idle_add(dst.notify_observers, *bbox)
示例#27
0
 def _start_task_queue_runner(self):
     """Begin processing the task queue, if not already going"""
     if self._task_queue_runner_id is not None:
         return
     idler_id = GLib.idle_add(self._task_queue_runner_cb)
     self._task_queue_runner_id = idler_id
示例#28
0
 def _preview_area_modified_cb(self, preview_model, x, y, w, h):
     """Handles changes made to the preview canvas"""
     self._preview_modified = True
     GLib.idle_add(self._update_widgets)
示例#29
0
    def build_from_layout(self, desc, init_sizes_state=None):
        """Loads groups and pages from a layout description

        :param desc: stack definition
        :type desc: dict
        :param init_sizes_state: toplevel window state transition on
            which to set the group dividers' initial positions. If left
            unset, set the sizes immediately.
        :type init_sizes_state: Gdk.WindowState
        :rtype: int
        :returns: the number of groups added

        The `desc` parameter has the following keys and values:

        * w: integer width (ignored here)
        * h: integer height (ignored here)
        * groups: list of group definitions - see below

        Width and height may be of relevance to the parent widget, but are not
        consumed by this method. `get_layout()` writes them, however.  Each
        group definition is a dict with the following keys and values.

        * tools: a list of tool definitions - see below
        * h: integer height: used here to set the height of the group
        * w: integer width (ignored here)

        Each tool definition is a tuple of the form (GTYPENAME,*CONSTRUCTARGS).
        GTYPENAME is a string containing a GType name which is used for finding
        and constructing the tool instance. CONSTRUCTARGS is currently ignored.

        """
        next_nb = self._get_first_notebook()
        factory = self.workspace._tool_widgets
        num_groups_added = 0
        for group_desc in desc.get("groups", []):
            assert next_nb.get_n_pages() == 0
            # Only add unique tool widgets. Assume this is being called on
            # startup, with an initially empty factory cache.
            tool_widgets = []
            for tool_desc in group_desc.get("tools", []):
                if factory.cache_has(*tool_desc):
                    logger.warning("Duplicate entry %r ignored", tool_desc)
                    continue
                logger.debug("build_from_layout: building tool %r", tool_desc)
                try:
                    tool_widget = factory.get(*tool_desc)
                    tool_widgets.append(tool_widget)
                except objfactory.ConstructError as ex:
                    logger.error("build_from_layout: %s", ex.message)
            # Group might be empty if construction fails or if everything's a
            # duplicate.
            if not tool_widgets:
                logger.debug("Empty tab group in workspace, not added")
                continue
            # We have something to add, so create a new Notebook with the
            # pages, and move the insert ref
            nb = next_nb
            next_nb = self._append_new_placeholder(nb)
            for tool_widget in tool_widgets:
                nb.append_tool_widget_page(tool_widget)
                if self.workspace:
                    GLib.idle_add(
                        self.workspace.tool_widget_added,
                        tool_widget,
                    )
            active_page = group_desc.get("active_page", -1)
            nb.set_current_page(active_page)
            num_groups_added += 1
            # Position the divider between the new notebook and the next.
            group_min_h = 1
            group_h = int(group_desc.get("h", group_min_h))
            group_h = max(group_min_h, group_h)
            nb_parent = nb.get_parent()
            assert isinstance(nb_parent, ToolStack._Paned)
            nb_parent._initial_divider_position = group_h
        return num_groups_added
示例#30
0
 def _page_removed_cb(self, notebook, child, page_num):
     GLib.idle_add(self._toolstack._update_structure)