9578053 Jan 22 2022 distfiles.gentoo.org/distfiles/gajim-1.3.3-2.tar.gz
This commit is contained in:
parent
a5b3822651
commit
4c1b226bff
1045 changed files with 753037 additions and 18 deletions
715
gajim/gtk/avatar_selector.py
Executable file
715
gajim/gtk/avatar_selector.py
Executable file
|
|
@ -0,0 +1,715 @@
|
|||
# This is a port of um-crop-area.c from GNOME’s 'Cheese' application, see
|
||||
# https://gitlab.gnome.org/GNOME/cheese/-/blob/3.34.0/libcheese/um-crop-area.c
|
||||
#
|
||||
# This file is part of Gajim.
|
||||
#
|
||||
# Gajim is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published
|
||||
# by the Free Software Foundation; version 3 only.
|
||||
#
|
||||
# Gajim is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import logging
|
||||
from enum import IntEnum
|
||||
from enum import unique
|
||||
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import GdkPixbuf
|
||||
from gi.repository import GLib
|
||||
from gi.repository import Gtk
|
||||
import cairo
|
||||
|
||||
from gajim.common.const import AvatarSize
|
||||
from gajim.common.i18n import _
|
||||
from gajim.common.helpers import get_file_path_from_dnd_dropped_uri
|
||||
|
||||
from .util import scale_with_ratio
|
||||
|
||||
log = logging.getLogger('gajim.gui.avatar_selector')
|
||||
|
||||
|
||||
@unique
|
||||
class Loc(IntEnum):
|
||||
OUTSIDE = 0
|
||||
INSIDE = 1
|
||||
TOP = 2
|
||||
TOP_LEFT = 3
|
||||
TOP_RIGHT = 4
|
||||
BOTTOM = 5
|
||||
BOTTOM_LEFT = 6
|
||||
BOTTOM_RIGHT = 7
|
||||
LEFT = 8
|
||||
RIGHT = 9
|
||||
|
||||
|
||||
@unique
|
||||
class Range(IntEnum):
|
||||
BELOW = 0
|
||||
LOWER = 1
|
||||
BETWEEN = 2
|
||||
UPPER = 3
|
||||
ABOVE = 4
|
||||
|
||||
|
||||
class AvatarSelector(Gtk.Box):
|
||||
def __init__(self):
|
||||
Gtk.Box.__init__(self)
|
||||
self.set_orientation(Gtk.Orientation.VERTICAL)
|
||||
self.get_style_context().add_class('padding-18')
|
||||
|
||||
uri_entry = Gtk.TargetEntry.new(
|
||||
'text/uri-list', Gtk.TargetFlags.OTHER_APP, 80)
|
||||
dst_targets = Gtk.TargetList.new([uri_entry])
|
||||
|
||||
self.drag_dest_set(
|
||||
Gtk.DestDefaults.ALL,
|
||||
[uri_entry],
|
||||
Gdk.DragAction.COPY | Gdk.DragAction.MOVE)
|
||||
self.drag_dest_set_target_list(dst_targets)
|
||||
self.connect('drag-data-received', self._on_drag_data_received)
|
||||
|
||||
self._crop_area = CropArea()
|
||||
self._crop_area.set_vexpand(True)
|
||||
self.add(self._crop_area)
|
||||
|
||||
self._helper_label = Gtk.Label(
|
||||
label=_('Select a picture or drop it here'))
|
||||
self._helper_label.get_style_context().add_class('bold')
|
||||
self._helper_label.get_style_context().add_class('dim-label')
|
||||
self._helper_label.set_vexpand(True)
|
||||
self._helper_label.set_no_show_all(True)
|
||||
self._helper_label.show()
|
||||
self.add(self._helper_label)
|
||||
|
||||
self.show_all()
|
||||
|
||||
def prepare_crop_area(self, path):
|
||||
pixbuf = self._get_pixbuf_from_path(path)
|
||||
self._crop_area.set_pixbuf(pixbuf)
|
||||
self._helper_label.hide()
|
||||
self._crop_area.show()
|
||||
|
||||
def _on_drag_data_received(self, _widget, _context, _x_coord, _y_coord,
|
||||
selection, target_type, _timestamp):
|
||||
if not selection.get_data():
|
||||
return
|
||||
|
||||
if target_type == 80:
|
||||
uri_split = selection.get_uris() # Might be more than one
|
||||
path = get_file_path_from_dnd_dropped_uri(uri_split[0])
|
||||
if not os.path.isfile(path):
|
||||
return
|
||||
self.prepare_crop_area(path)
|
||||
|
||||
@staticmethod
|
||||
def _get_pixbuf_from_path(path):
|
||||
try:
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file(path)
|
||||
return pixbuf
|
||||
except GLib.Error as err:
|
||||
log.error('Unable to load file %s: %s', path, str(err))
|
||||
return None
|
||||
|
||||
def get_prepared(self):
|
||||
return bool(self._crop_area.get_pixbuf())
|
||||
|
||||
@staticmethod
|
||||
def _scale_for_publish(pixbuf):
|
||||
width = pixbuf.get_width()
|
||||
height = pixbuf.get_height()
|
||||
if width > AvatarSize.PUBLISH or height > AvatarSize.PUBLISH:
|
||||
# Scale only down, never up
|
||||
width, height = scale_with_ratio(AvatarSize.PUBLISH, width, height)
|
||||
pixbuf = pixbuf.scale_simple(width,
|
||||
height,
|
||||
GdkPixbuf.InterpType.BILINEAR)
|
||||
return pixbuf, width, height
|
||||
|
||||
def get_avatar_surface(self):
|
||||
pixbuf = self._crop_area.get_pixbuf()
|
||||
if pixbuf is None:
|
||||
return None
|
||||
scaled, width, height = self._scale_for_publish(pixbuf)
|
||||
|
||||
return Gdk.cairo_surface_create_from_pixbuf(
|
||||
scaled, self.get_scale_factor()), width, height
|
||||
|
||||
def get_avatar_bytes(self):
|
||||
pixbuf = self._crop_area.get_pixbuf()
|
||||
if pixbuf is None:
|
||||
return False, None, 0, 0
|
||||
scaled, width, height = self._scale_for_publish(pixbuf)
|
||||
|
||||
success, data = scaled.save_to_bufferv('png', [], [])
|
||||
return success, data, width, height
|
||||
|
||||
|
||||
class CropArea(Gtk.DrawingArea):
|
||||
def __init__(self):
|
||||
Gtk.DrawingArea.__init__(self)
|
||||
self.set_no_show_all(True)
|
||||
self.add_events(
|
||||
Gdk.EventMask.BUTTON_PRESS_MASK |
|
||||
Gdk.EventMask.BUTTON_RELEASE_MASK |
|
||||
Gdk.EventMask.POINTER_MOTION_MASK)
|
||||
|
||||
self._image = Gdk.Rectangle()
|
||||
self._crop = Gdk.Rectangle()
|
||||
self._pixbuf = None
|
||||
self._browse_pixbuf = None
|
||||
self._color_shifted_pixbuf = None
|
||||
self._current_cursor = None
|
||||
|
||||
self._scale = float(0.0)
|
||||
self._image.x = 0
|
||||
self._image.y = 0
|
||||
self._image.width = 0
|
||||
self._image.height = 0
|
||||
self._active_region = Loc.OUTSIDE
|
||||
self._last_press_x = -1
|
||||
self._last_press_y = -1
|
||||
self._base_width = 10
|
||||
self._base_height = 10
|
||||
self._aspect = float(1.0)
|
||||
|
||||
self.set_size_request(self._base_width, self._base_height)
|
||||
|
||||
self.connect('draw', self._on_draw)
|
||||
self.connect('button-press-event', self._on_button_press)
|
||||
self.connect('button-release-event', self._on_button_release)
|
||||
self.connect('motion-notify-event', self._on_motion_notify)
|
||||
|
||||
def set_min_size(self, width, height):
|
||||
self._base_width = width
|
||||
self._base_height = height
|
||||
self.set_size_request(self._base_width, self._base_height)
|
||||
|
||||
if self._aspect > 0:
|
||||
self._aspect = self._base_width / self._base_height
|
||||
|
||||
def set_contstrain_aspect(self, constrain):
|
||||
if constrain:
|
||||
self._aspect = self._base_width / self._base_height
|
||||
else:
|
||||
self._aspect = -1
|
||||
|
||||
def set_pixbuf(self, pixbuf):
|
||||
if pixbuf:
|
||||
self._browse_pixbuf = pixbuf
|
||||
width = pixbuf.get_width()
|
||||
height = pixbuf.get_height()
|
||||
else:
|
||||
width = 0
|
||||
height = 0
|
||||
|
||||
self._crop.width = 2 * self._base_width
|
||||
self._crop.height = 2 * self._base_height
|
||||
self._crop.x = abs((width - self._crop.width) / 2)
|
||||
self._crop.y = abs((height - self._crop.height) / 2)
|
||||
|
||||
self._scale = 0.0
|
||||
self._image.x = 0
|
||||
self._image.y = 0
|
||||
self._image.width = 0
|
||||
self._image.height = 0
|
||||
|
||||
self.queue_draw()
|
||||
|
||||
def get_pixbuf(self):
|
||||
if self._browse_pixbuf is None:
|
||||
return None
|
||||
|
||||
width = self._browse_pixbuf.get_width()
|
||||
height = self._browse_pixbuf.get_height()
|
||||
width = min(self._crop.width, width - self._crop.x)
|
||||
height = min(self._crop.height, height - self._crop.y)
|
||||
|
||||
if width <= 0 or height <= 0:
|
||||
return None
|
||||
|
||||
return GdkPixbuf.Pixbuf.new_subpixbuf(
|
||||
self._browse_pixbuf, self._crop.x, self._crop.y, width, height)
|
||||
|
||||
def _on_draw(self, _widget, context):
|
||||
if self._browse_pixbuf is None:
|
||||
return False
|
||||
|
||||
self._update_pixbufs()
|
||||
|
||||
width = self._pixbuf.get_width()
|
||||
height = self._pixbuf.get_height()
|
||||
crop = self._crop_to_widget()
|
||||
|
||||
ix = self._image.x
|
||||
iy = self._image.y
|
||||
|
||||
Gdk.cairo_set_source_pixbuf(
|
||||
context, self._color_shifted_pixbuf, ix, iy)
|
||||
context.rectangle(
|
||||
ix,
|
||||
iy,
|
||||
width,
|
||||
crop.y - iy)
|
||||
context.rectangle(
|
||||
ix,
|
||||
crop.y,
|
||||
crop.x - ix,
|
||||
crop.height)
|
||||
context.rectangle(
|
||||
crop.x + crop.width,
|
||||
crop.y,
|
||||
width - crop.width - (crop.x - ix),
|
||||
crop.height)
|
||||
context.rectangle(
|
||||
ix,
|
||||
crop.y + crop.height,
|
||||
width,
|
||||
height - crop.height - (crop.y - iy))
|
||||
context.fill()
|
||||
|
||||
Gdk.cairo_set_source_pixbuf(context, self._pixbuf, ix, iy)
|
||||
context.rectangle(crop.x, crop.y, crop.width, crop.height)
|
||||
context.fill()
|
||||
|
||||
if self._active_region != Loc.OUTSIDE:
|
||||
context.set_source_rgb(150, 150, 150)
|
||||
context.set_line_width(1.0)
|
||||
x1 = crop.x + crop.width / 3.0
|
||||
x2 = crop.x + 2 * crop.width / 3.0
|
||||
y1 = crop.y + crop.height / 3.0
|
||||
y2 = crop.y + 2 * crop.height / 3.0
|
||||
|
||||
context.move_to(x1 + 0.5, crop.y)
|
||||
context.line_to(x1 + 0.5, crop.y + crop.height)
|
||||
|
||||
context.move_to(x2 + 0.5, crop.y)
|
||||
context.line_to(x2 + 0.5, crop.y + crop.height)
|
||||
|
||||
context.move_to(crop.x, y1 + 0.5)
|
||||
context.line_to(crop.x + crop.width, y1 + 0.5)
|
||||
|
||||
context.move_to(crop.x, y2 + 0.5)
|
||||
context.line_to(crop.x + crop.width, y2 + 0.5)
|
||||
context.stroke()
|
||||
|
||||
context.set_source_rgb(1, 1, 1)
|
||||
context.set_line_width(1.0)
|
||||
|
||||
context.rectangle(
|
||||
crop.x + 0.5,
|
||||
crop.y + 0.5,
|
||||
crop.width - 1.0,
|
||||
crop.height - 1.0)
|
||||
context.stroke()
|
||||
|
||||
context.set_source_rgb(1, 1, 1)
|
||||
context.set_line_width(2.0)
|
||||
context.rectangle(
|
||||
crop.x + 2.0,
|
||||
crop.y + 2.0,
|
||||
crop.width - 4.0,
|
||||
crop.height - 4.0)
|
||||
context.stroke()
|
||||
|
||||
return False
|
||||
|
||||
def _on_button_press(self, _widget, event):
|
||||
if self._browse_pixbuf is None:
|
||||
return False
|
||||
|
||||
crop = self._crop_to_widget()
|
||||
|
||||
self._last_press_x = (event.x - self._image.x) / self._scale
|
||||
self._last_press_y = (event.y - self._image.y) / self._scale
|
||||
self._active_region = self._find_location(crop, event.x, event.y)
|
||||
|
||||
self.queue_draw_area(
|
||||
crop.x - 1, crop.y - 1, crop.width + 2, crop.height + 2)
|
||||
|
||||
return False
|
||||
|
||||
def _on_button_release(self, _widget, _event):
|
||||
if self._browse_pixbuf is None:
|
||||
return False
|
||||
|
||||
crop = self._crop_to_widget()
|
||||
self._last_press_x = -1
|
||||
self._last_press_y = -1
|
||||
self._active_region = Loc.OUTSIDE
|
||||
|
||||
self.queue_draw_area(
|
||||
crop.x - 1, crop.y - 1, crop.width + 2, crop.height + 2)
|
||||
|
||||
return False
|
||||
|
||||
def _on_motion_notify(self, _widget, event):
|
||||
# pylint: disable=too-many-boolean-expressions
|
||||
# pylint: disable=too-many-branches
|
||||
# pylint: disable=too-many-statements
|
||||
if self._browse_pixbuf is None:
|
||||
return False
|
||||
|
||||
self._update_cursor(event.x, event.y)
|
||||
|
||||
damage = self._crop_to_widget()
|
||||
self.queue_draw_area(
|
||||
damage.x - 1, damage.y - 1, damage.width + 2, damage.height + 2)
|
||||
|
||||
pb_width = self._browse_pixbuf.get_width()
|
||||
pb_height = self._browse_pixbuf.get_height()
|
||||
|
||||
x_coord = int((event.x - self._image.x) / self._scale)
|
||||
y_coord = int((event.y - self._image.y) / self._scale)
|
||||
|
||||
delta_x = int(x_coord - self._last_press_x)
|
||||
delta_y = int(y_coord - self._last_press_y)
|
||||
self._last_press_x = x_coord
|
||||
self._last_press_y = y_coord
|
||||
|
||||
left = int(self._crop.x)
|
||||
right = int(self._crop.x + self._crop.width - 1)
|
||||
top = int(self._crop.y)
|
||||
bottom = int(self._crop.y + self._crop.height - 1)
|
||||
|
||||
center_x = float((left + right) / 2.0)
|
||||
center_y = float((top + bottom) / 2.0)
|
||||
|
||||
if self._active_region == Loc.INSIDE:
|
||||
width = right - left + 1
|
||||
height = bottom - top + 1
|
||||
|
||||
left += delta_x
|
||||
right += delta_x
|
||||
top += delta_y
|
||||
bottom += delta_y
|
||||
|
||||
if left < 0:
|
||||
left = 0
|
||||
if top < 0:
|
||||
top = 0
|
||||
if right > pb_width:
|
||||
right = pb_width
|
||||
if bottom > pb_height:
|
||||
bottom = pb_height
|
||||
|
||||
adj_width = int(right - left + 1)
|
||||
adj_height = int(bottom - top + 1)
|
||||
if adj_width != width:
|
||||
if delta_x < 0:
|
||||
right = left + width - 1
|
||||
else:
|
||||
left = right - width + 1
|
||||
|
||||
if adj_height != height:
|
||||
if delta_y < 0:
|
||||
bottom = top + height - 1
|
||||
else:
|
||||
top = bottom - height + 1
|
||||
|
||||
elif self._active_region == Loc.TOP_LEFT:
|
||||
if self._aspect < 0:
|
||||
top = y_coord
|
||||
left = x_coord
|
||||
elif y_coord < self._eval_radial_line(
|
||||
center_x, center_y, left, top, x_coord):
|
||||
top = y_coord
|
||||
new_width = float((bottom - top) * self._aspect)
|
||||
left = right - new_width
|
||||
else:
|
||||
left = x_coord
|
||||
new_height = float((right - left) / self._aspect)
|
||||
top = bottom - new_height
|
||||
|
||||
elif self._active_region == Loc.TOP:
|
||||
top = y_coord
|
||||
if self._aspect > 0:
|
||||
new_width = float((bottom - top) * self._aspect)
|
||||
right = left + new_width
|
||||
|
||||
elif self._active_region == Loc.TOP_RIGHT:
|
||||
if self._aspect < 0:
|
||||
top = y_coord
|
||||
right = x_coord
|
||||
elif y_coord < self._eval_radial_line(
|
||||
center_x, center_y, right, top, x_coord):
|
||||
top = y_coord
|
||||
new_width = float((bottom - top) * self._aspect)
|
||||
right = left + new_width
|
||||
else:
|
||||
right = x_coord
|
||||
new_height = float((right - left) / self._aspect)
|
||||
top = bottom - new_height
|
||||
|
||||
elif self._active_region == Loc.LEFT:
|
||||
left = x_coord
|
||||
if self._aspect > 0:
|
||||
new_height = float((right - left) / self._aspect)
|
||||
bottom = top + new_height
|
||||
|
||||
elif self._active_region == Loc.BOTTOM_LEFT:
|
||||
if self._aspect < 0:
|
||||
bottom = y_coord
|
||||
left = x_coord
|
||||
elif y_coord < self._eval_radial_line(
|
||||
center_x, center_y, left, bottom, x_coord):
|
||||
left = x_coord
|
||||
new_height = float((right - left) / self._aspect)
|
||||
bottom = top + new_height
|
||||
else:
|
||||
bottom = y_coord
|
||||
new_width = float((bottom - top) * self._aspect)
|
||||
left = right - new_width
|
||||
|
||||
elif self._active_region == Loc.RIGHT:
|
||||
right = x_coord
|
||||
if self._aspect > 0:
|
||||
new_height = float((right - left) / self._aspect)
|
||||
bottom = top + new_height
|
||||
|
||||
elif self._active_region == Loc.BOTTOM_RIGHT:
|
||||
if self._aspect < 0:
|
||||
bottom = y_coord
|
||||
right = x_coord
|
||||
elif y_coord < self._eval_radial_line(
|
||||
center_x, center_y, right, bottom, x_coord):
|
||||
right = x_coord
|
||||
new_height = float((right - left) / self._aspect)
|
||||
bottom = top + new_height
|
||||
else:
|
||||
bottom = y_coord
|
||||
new_width = float((bottom - top) * self._aspect)
|
||||
right = left + new_width
|
||||
|
||||
elif self._active_region == Loc.BOTTOM:
|
||||
bottom = y_coord
|
||||
if self._aspect > 0:
|
||||
new_width = float((bottom - top) * self._aspect)
|
||||
right = left + new_width
|
||||
else:
|
||||
return False
|
||||
|
||||
min_width = int(self._base_width / self._scale)
|
||||
min_height = int(self._base_height / self._scale)
|
||||
|
||||
width = right - left + 1
|
||||
height = bottom - top + 1
|
||||
if self._aspect < 0:
|
||||
if left < 0:
|
||||
left = 0
|
||||
if top < 0:
|
||||
top = 0
|
||||
if right > pb_width:
|
||||
right = pb_width
|
||||
if bottom > pb_height:
|
||||
bottom = pb_height
|
||||
|
||||
width = right - left + 1
|
||||
height = bottom - top + 1
|
||||
|
||||
if self._active_region in (
|
||||
Loc.LEFT, Loc.TOP_LEFT, Loc.BOTTOM_LEFT):
|
||||
if width < min_width:
|
||||
left = right - min_width
|
||||
elif self._active_region in (
|
||||
Loc.RIGHT, Loc.TOP_RIGHT, Loc.BOTTOM_RIGHT):
|
||||
if width < min_width:
|
||||
right = left + min_width
|
||||
|
||||
if self._active_region in (
|
||||
Loc.TOP, Loc.TOP_LEFT, Loc.TOP_RIGHT):
|
||||
if height < min_height:
|
||||
top = bottom - min_height
|
||||
elif self._active_region in (
|
||||
Loc.BOTTOM, Loc.BOTTOM_LEFT, Loc.BOTTOM_RIGHT):
|
||||
if height < min_height:
|
||||
bottom = top + min_height
|
||||
|
||||
else:
|
||||
if (left < 0 or top < 0 or
|
||||
right > pb_width or bottom > pb_height or
|
||||
width < min_width or height < min_height):
|
||||
left = self._crop.x
|
||||
right = self._crop.x + self._crop.width - 1
|
||||
top = self._crop.y
|
||||
bottom = self._crop.y + self._crop.height - 1
|
||||
|
||||
self._crop.x = left
|
||||
self._crop.y = top
|
||||
self._crop.width = right - left + 1
|
||||
self._crop.height = bottom - top + 1
|
||||
|
||||
damage = self._crop_to_widget()
|
||||
self.queue_draw_area(
|
||||
damage.x - 1, damage.y - 1, damage.width + 2, damage.height + 2)
|
||||
|
||||
return False
|
||||
|
||||
def _update_pixbufs(self):
|
||||
allocation = self.get_allocation()
|
||||
width = self._browse_pixbuf.get_width()
|
||||
height = self._browse_pixbuf.get_height()
|
||||
|
||||
scale = allocation.height / float(height)
|
||||
if scale * width > allocation.width:
|
||||
scale = allocation.width / float(width)
|
||||
|
||||
dest_width = width * scale
|
||||
dest_height = height * scale
|
||||
|
||||
if (self._pixbuf is None or
|
||||
self._pixbuf.get_width != allocation.width or
|
||||
self._pixbuf.get_height != allocation.height):
|
||||
|
||||
self._pixbuf = GdkPixbuf.Pixbuf.new(
|
||||
GdkPixbuf.Colorspace.RGB,
|
||||
self._browse_pixbuf.get_has_alpha(),
|
||||
8,
|
||||
dest_width,
|
||||
dest_height)
|
||||
self._pixbuf.fill(0x0)
|
||||
|
||||
self._browse_pixbuf.scale(
|
||||
self._pixbuf,
|
||||
0,
|
||||
0,
|
||||
dest_width,
|
||||
dest_height,
|
||||
0,
|
||||
0,
|
||||
scale,
|
||||
scale,
|
||||
GdkPixbuf.InterpType.BILINEAR)
|
||||
|
||||
self._generate_color_shifted_pixbuf()
|
||||
|
||||
if self._scale == 0.0:
|
||||
scale_to_80 = float(min(
|
||||
(self._pixbuf.get_width() * 0.8 / self._base_width),
|
||||
(self._pixbuf.get_height() * 0.8 / self._base_height)))
|
||||
scale_to_image = float(min(
|
||||
(dest_width / self._base_width),
|
||||
(dest_height / self._base_height)))
|
||||
crop_scale = float(min(scale_to_80, scale_to_image))
|
||||
|
||||
self._crop.width = crop_scale * self._base_width / scale
|
||||
self._crop.height = crop_scale * self._base_height / scale
|
||||
self._crop.x = (
|
||||
self._browse_pixbuf.get_width() - self._crop.width) / 2
|
||||
self._crop.y = (
|
||||
self._browse_pixbuf.get_height() - self._crop.height) / 2
|
||||
|
||||
self._scale = scale
|
||||
self._image.x = (allocation.width - dest_width) / 2
|
||||
self._image.y = (allocation.height - dest_height) / 2
|
||||
self._image.width = dest_width
|
||||
self._image.height = dest_height
|
||||
|
||||
def _crop_to_widget(self):
|
||||
crop = Gdk.Rectangle()
|
||||
crop.x = self._image.x + self._crop.x * self._scale
|
||||
crop.y = self._image.y + self._crop.y * self._scale
|
||||
crop.width = self._crop.width * self._scale
|
||||
crop.height = self._crop.height * self._scale
|
||||
return crop
|
||||
|
||||
def _update_cursor(self, x_coord, y_coord):
|
||||
region = self._active_region
|
||||
if self._active_region == Loc.OUTSIDE:
|
||||
crop = self._crop_to_widget()
|
||||
region = self._find_location(crop, x_coord, y_coord)
|
||||
|
||||
if region == Loc.TOP_LEFT:
|
||||
cursor_type = Gdk.CursorType.TOP_LEFT_CORNER
|
||||
elif region == Loc.TOP:
|
||||
cursor_type = Gdk.CursorType.TOP_SIDE
|
||||
elif region == Loc.TOP_RIGHT:
|
||||
cursor_type = Gdk.CursorType.TOP_RIGHT_CORNER
|
||||
elif region == Loc.LEFT:
|
||||
cursor_type = Gdk.CursorType.LEFT_SIDE
|
||||
elif region == Loc.INSIDE:
|
||||
cursor_type = Gdk.CursorType.FLEUR
|
||||
elif region == Loc.RIGHT:
|
||||
cursor_type = Gdk.CursorType.RIGHT_SIDE
|
||||
elif region == Loc.BOTTOM_LEFT:
|
||||
cursor_type = Gdk.CursorType.BOTTOM_LEFT_CORNER
|
||||
elif region == Loc.BOTTOM:
|
||||
cursor_type = Gdk.CursorType.BOTTOM_SIDE
|
||||
elif region == Loc.BOTTOM_RIGHT:
|
||||
cursor_type = Gdk.CursorType.BOTTOM_RIGHT_CORNER
|
||||
else: # Loc.OUTSIDE
|
||||
cursor_type = Gdk.CursorType.LEFT_PTR
|
||||
|
||||
if cursor_type is not self._current_cursor:
|
||||
cursor = Gdk.Cursor.new_for_display(
|
||||
Gdk.Display.get_default(),
|
||||
cursor_type)
|
||||
self.get_window().set_cursor(cursor)
|
||||
self._current_cursor = cursor_type
|
||||
|
||||
@staticmethod
|
||||
def _eval_radial_line(center_x, center_y, bounds_x, bounds_y, user_x):
|
||||
slope_y = float(bounds_y - center_y)
|
||||
slope_x = bounds_x - center_x
|
||||
if slope_y == 0 or slope_x == 0:
|
||||
# Prevent division by zero
|
||||
return 0
|
||||
|
||||
decision_slope = slope_y / slope_x
|
||||
decision_intercept = - float(decision_slope * bounds_x)
|
||||
return int(decision_slope * user_x + decision_intercept)
|
||||
|
||||
def _find_location(self, rect, x_coord, y_coord):
|
||||
# pylint: disable=line-too-long
|
||||
location = [
|
||||
[Loc.OUTSIDE, Loc.OUTSIDE, Loc.OUTSIDE, Loc.OUTSIDE, Loc.OUTSIDE],
|
||||
[Loc.OUTSIDE, Loc.TOP_LEFT, Loc.TOP, Loc.TOP_RIGHT, Loc.OUTSIDE],
|
||||
[Loc.OUTSIDE, Loc.LEFT, Loc.INSIDE, Loc.RIGHT, Loc.OUTSIDE],
|
||||
[Loc.OUTSIDE, Loc.BOTTOM_LEFT, Loc.BOTTOM, Loc.BOTTOM_RIGHT, Loc.OUTSIDE],
|
||||
[Loc.OUTSIDE, Loc.OUTSIDE, Loc.OUTSIDE, Loc.OUTSIDE, Loc.OUTSIDE],
|
||||
]
|
||||
# pylint: enable=line-too-long
|
||||
|
||||
x_range = self._find_range(x_coord, rect.x, rect.x + rect.width)
|
||||
y_range = self._find_range(y_coord, rect.y, rect.y + rect.height)
|
||||
|
||||
return location[y_range][x_range]
|
||||
|
||||
@staticmethod
|
||||
def _find_range(coord, min_v, max_v):
|
||||
tolerance = 12
|
||||
if coord < min_v - tolerance:
|
||||
return Range.BELOW
|
||||
if coord <= min_v + tolerance:
|
||||
return Range.LOWER
|
||||
if coord < max_v - tolerance:
|
||||
return Range.BETWEEN
|
||||
if coord <= max_v + tolerance:
|
||||
return Range.UPPER
|
||||
return Range.ABOVE
|
||||
|
||||
def _generate_color_shifted_pixbuf(self):
|
||||
# pylint: disable=no-member
|
||||
surface = cairo.ImageSurface(
|
||||
cairo.Format.ARGB32,
|
||||
self._pixbuf.get_width(),
|
||||
self._pixbuf.get_height())
|
||||
context = cairo.Context(surface)
|
||||
# pylint: enable=no-member
|
||||
|
||||
Gdk.cairo_set_source_pixbuf(context, self._pixbuf, 0, 0)
|
||||
context.paint()
|
||||
|
||||
context.rectangle(0, 0, 1, 1)
|
||||
context.set_source_rgba(0, 0, 0, 0.5)
|
||||
context.paint()
|
||||
|
||||
surface = context.get_target()
|
||||
self._color_shifted_pixbuf = Gdk.pixbuf_get_from_surface(
|
||||
surface, 0, 0, surface.get_width(), surface.get_height())
|
||||
Loading…
Add table
Add a link
Reference in a new issue