summaryrefslogtreecommitdiffstats
path: root/src/ui/widget/canvas-grid.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/widget/canvas-grid.cpp')
-rw-r--r--src/ui/widget/canvas-grid.cpp419
1 files changed, 419 insertions, 0 deletions
diff --git a/src/ui/widget/canvas-grid.cpp b/src/ui/widget/canvas-grid.cpp
new file mode 100644
index 0000000..460b606
--- /dev/null
+++ b/src/ui/widget/canvas-grid.cpp
@@ -0,0 +1,419 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*
+ * Author:
+ * Tavmjong Bah
+ *
+ * Rewrite of code originally in desktop-widget.cpp.
+ *
+ * Copyright (C) 2020 Tavmjong Bah
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+// The scrollbars, and canvas are tightly coupled so it makes sense to have a dedicated
+// widget to handle their interactions. The buttons are along for the ride. I don't see
+// how to add the buttons easily via a .ui file (which would allow the user to put any
+// buttons they want in their place).
+
+#include <glibmm/i18n.h>
+#include <gtkmm/enums.h>
+#include <gtkmm/label.h>
+
+#include "canvas-grid.h"
+
+#include "desktop.h" // Hopefully temp.
+#include "desktop-events.h" // Hopefully temp.
+
+#include "display/control/canvas-item-drawing.h" // sticky
+
+#include "page-manager.h"
+
+#include "ui/dialog/command-palette.h"
+#include "ui/icon-loader.h"
+#include "ui/widget/canvas.h"
+#include "ui/widget/canvas-notice.h"
+#include "ui/widget/ink-ruler.h"
+#include "io/resource.h"
+
+#include "widgets/desktop-widget.h" // Hopefully temp.
+
+namespace Inkscape {
+namespace UI {
+namespace Widget {
+
+CanvasGrid::CanvasGrid(SPDesktopWidget *dtw)
+{
+ _dtw = dtw;
+ set_name("CanvasGrid");
+
+ // Canvas
+ _canvas = std::make_unique<Inkscape::UI::Widget::Canvas>();
+ _canvas->set_hexpand(true);
+ _canvas->set_vexpand(true);
+ _canvas->set_can_focus(true);
+ _canvas->signal_event().connect(sigc::mem_fun(*this, &CanvasGrid::SignalEvent)); // TEMP
+
+ // Command palette
+ _command_palette = std::make_unique<Inkscape::UI::Dialog::CommandPalette>();
+
+ // Notice overlay, note using unique_ptr will cause destruction race conditions
+ _notice = CanvasNotice::create();
+
+ // Canvas overlay
+ _canvas_overlay.add(*_canvas);
+ _canvas_overlay.add_overlay(*_command_palette->get_base_widget());
+ _canvas_overlay.add_overlay(*_notice);
+
+ // Horizontal Ruler
+ _hruler = std::make_unique<Inkscape::UI::Widget::Ruler>(Gtk::ORIENTATION_HORIZONTAL);
+ _hruler->add_track_widget(*_canvas);
+ _hruler->set_hexpand(true);
+ _hruler->show();
+ // Tooltip/Unit set elsewhere
+
+ // Vertical Ruler
+ _vruler = std::make_unique<Inkscape::UI::Widget::Ruler>(Gtk::ORIENTATION_VERTICAL);
+ _vruler->add_track_widget(*_canvas);
+ _vruler->set_vexpand(true);
+ _vruler->show();
+ // Tooltip/Unit set elsewhere.
+
+ // Guide Lock
+ _guide_lock.set_name("LockGuides");
+ _guide_lock.add(*Gtk::make_managed<Gtk::Image>("object-locked", Gtk::ICON_SIZE_MENU));
+ // To be replaced by Gio::Action:
+ _guide_lock.signal_toggled().connect(sigc::mem_fun(*_dtw, &SPDesktopWidget::update_guides_lock));
+ _guide_lock.set_tooltip_text(_("Toggle lock of all guides in the document"));
+ // Subgrid
+ _subgrid.attach(_guide_lock, 0, 0, 1, 1);
+ _subgrid.attach(*_vruler, 0, 1, 1, 1);
+ _subgrid.attach(*_hruler, 1, 0, 1, 1);
+ _subgrid.attach(_canvas_overlay, 1, 1, 1, 1);
+
+ // Horizontal Scrollbar
+ _hadj = Gtk::Adjustment::create(0.0, -4000.0, 4000.0, 10.0, 100.0, 4.0);
+ _hadj->signal_value_changed().connect(sigc::mem_fun(*_dtw, &SPDesktopWidget::on_adjustment_value_changed));
+ _hscrollbar = Gtk::Scrollbar(_hadj, Gtk::ORIENTATION_HORIZONTAL);
+ _hscrollbar.set_name("CanvasScrollbar");
+ _hscrollbar.set_hexpand(true);
+
+ // Vertical Scrollbar
+ _vadj = Gtk::Adjustment::create(0.0, -4000.0, 4000.0, 10.0, 100.0, 4.0);
+ _vadj->signal_value_changed().connect(sigc::mem_fun(*_dtw, &SPDesktopWidget::on_adjustment_value_changed));
+ _vscrollbar = Gtk::Scrollbar(_vadj, Gtk::ORIENTATION_VERTICAL);
+ _vscrollbar.set_name("CanvasScrollbar");
+ _vscrollbar.set_vexpand(true);
+
+ // CMS Adjust (To be replaced by Gio::Action)
+ _cms_adjust.set_name("CMS_Adjust");
+ _cms_adjust.add(*Gtk::make_managed<Gtk::Image>("color-management", Gtk::ICON_SIZE_MENU));
+ // Can't access via C++ API, fixed in Gtk4.
+ gtk_actionable_set_action_name( GTK_ACTIONABLE(_cms_adjust.gobj()), "win.canvas-color-manage");
+ _cms_adjust.set_tooltip_text(_("Toggle color-managed display for this document window"));
+
+ // popover with some common display mode related options
+ auto builder = Gtk::Builder::create_from_file(Inkscape::IO::Resource::get_filename(Inkscape::IO::Resource::UIS, "display-popup.glade"));
+ _display_popup = builder;
+ Gtk::Popover* popover;
+ _display_popup->get_widget("popover", popover);
+ Gtk::CheckButton* sticky_zoom;
+ _display_popup->get_widget("zoom-resize", sticky_zoom);
+ // To be replaced by Gio::Action:
+ sticky_zoom->signal_toggled().connect([=](){ _dtw->sticky_zoom_toggled(); });
+ _quick_actions.set_name("QuickActions");
+ _quick_actions.set_popover(*popover);
+ _quick_actions.set_image_from_icon_name("display-symbolic");
+ _quick_actions.set_direction(Gtk::ARROW_LEFT);
+ _quick_actions.set_tooltip_text(_("Display options"));
+
+ // Main grid
+ attach(_subgrid, 0, 0, 1, 2);
+ attach(_hscrollbar, 0, 2, 1, 1);
+ attach(_cms_adjust, 1, 2, 1, 1);
+ attach(_quick_actions, 1, 0, 1, 1);
+ attach(_vscrollbar, 1, 1, 1, 1);
+
+ // For creating guides, etc.
+ _hruler->signal_button_press_event().connect(
+ sigc::bind(sigc::mem_fun(*_dtw, &SPDesktopWidget::on_ruler_box_button_press_event), _hruler.get(), true));
+ _hruler->signal_button_release_event().connect(
+ sigc::bind(sigc::mem_fun(*_dtw, &SPDesktopWidget::on_ruler_box_button_release_event), _hruler.get(), true));
+ _hruler->signal_motion_notify_event().connect(
+ sigc::bind(sigc::mem_fun(*_dtw, &SPDesktopWidget::on_ruler_box_motion_notify_event), _hruler.get(), true));
+
+ // For creating guides, etc.
+ _vruler->signal_button_press_event().connect(
+ sigc::bind(sigc::mem_fun(*_dtw, &SPDesktopWidget::on_ruler_box_button_press_event), _vruler.get(), false));
+ _vruler->signal_button_release_event().connect(
+ sigc::bind(sigc::mem_fun(*_dtw, &SPDesktopWidget::on_ruler_box_button_release_event), _vruler.get(), false));
+ _vruler->signal_motion_notify_event().connect(
+ sigc::bind(sigc::mem_fun(*_dtw, &SPDesktopWidget::on_ruler_box_motion_notify_event), _vruler.get(), false));
+
+ show_all();
+}
+
+CanvasGrid::~CanvasGrid()
+{
+ _page_modified_connection.disconnect();
+ _page_selected_connection.disconnect();
+ _sel_modified_connection.disconnect();
+ _sel_changed_connection.disconnect();
+ _document = nullptr;
+ _notice = nullptr;
+}
+
+void CanvasGrid::on_realize() {
+ // actions should be available now
+
+ if (auto map = _dtw->get_action_map()) {
+ auto set_display_icon = [=]() {
+ Glib::ustring id;
+ auto mode = _canvas->get_render_mode();
+ switch (mode) {
+ case RenderMode::NORMAL: id = "display";
+ break;
+ case RenderMode::OUTLINE: id = "display-outline";
+ break;
+ case RenderMode::OUTLINE_OVERLAY: id = "display-outline-overlay";
+ break;
+ case RenderMode::VISIBLE_HAIRLINES: id = "display-enhance-stroke";
+ break;
+ case RenderMode::NO_FILTERS: id = "display-no-filter";
+ break;
+ default:
+ g_warning("Unknown display mode in canvas-grid");
+ break;
+ }
+
+ if (!id.empty()) {
+ // if CMS is ON show alternative icons
+ if (_canvas->get_cms_active()) {
+ id += "-alt";
+ }
+ _quick_actions.set_image_from_icon_name(id + "-symbolic");
+ }
+ };
+
+ set_display_icon();
+
+ // when display mode state changes, update icon
+ auto cms_action = Glib::RefPtr<Gio::SimpleAction>::cast_dynamic(map->lookup_action("canvas-color-manage"));
+ auto disp_action = Glib::RefPtr<Gio::SimpleAction>::cast_dynamic(map->lookup_action("canvas-display-mode"));
+
+ if (cms_action && disp_action) {
+ disp_action->signal_activate().connect([=](const Glib::VariantBase& state){ set_display_icon(); });
+ cms_action-> signal_activate().connect([=](const Glib::VariantBase& state){ set_display_icon(); });
+ }
+ else {
+ g_warning("No canvas-display-mode and/or canvas-color-manage action available to canvas-grid");
+ }
+ }
+ else {
+ g_warning("No action map available to canvas-grid");
+ }
+
+ parent_type::on_realize();
+}
+
+// TODO: remove when sticky zoom gets replaced by Gio::Action:
+Gtk::ToggleButton* CanvasGrid::GetStickyZoom() {
+ Gtk::CheckButton* sticky_zoom;
+ _display_popup->get_widget("zoom-resize", sticky_zoom);
+ return sticky_zoom;
+}
+
+// _dt2r should be a member of _canvas.
+// get_display_area should be a member of _canvas.
+void
+CanvasGrid::UpdateRulers()
+{
+ auto prefs = Inkscape::Preferences::get();
+ auto desktop = _dtw->desktop;
+ auto document = desktop->getDocument();
+ auto &pm = document->getPageManager();
+ auto sel = desktop->getSelection();
+
+ // Our connections to the document are handled with a lazy pattern to avoid
+ // having to refactor the SPDesktopWidget class. We know UpdateRulers is
+ // called in all situations when documents are loaded and replaced.
+ if (document != _document) {
+ _document = document;
+ _page_selected_connection = pm.connectPageSelected([=](SPPage *) { UpdateRulers(); });
+ _page_modified_connection = pm.connectPageModified([=](SPPage *) { UpdateRulers(); });
+ _sel_modified_connection = sel->connectModified([=](Inkscape::Selection *, int) { UpdateRulers(); });
+ _sel_changed_connection = sel->connectChanged([=](Inkscape::Selection *) { UpdateRulers(); });
+ }
+
+ Geom::Rect viewbox = desktop->get_display_area().bounds();
+ Geom::Rect startbox = viewbox;
+ if (prefs->getBool("/options/origincorrection/page", true)) {
+ // Move viewbox according to the selected page's position (if any)
+ startbox *= pm.getSelectedPageAffine().inverse();
+ }
+
+ // Scale and offset the ruler coordinates
+ // Use an integer box to align the ruler to the grid and page.
+ auto rulerbox = (startbox * Geom::Scale(_dtw->_dt2r));
+ _hruler->set_range(rulerbox.left(), rulerbox.right());
+ if (_dtw->desktop->is_yaxisdown()) {
+ _vruler->set_range(rulerbox.top(), rulerbox.bottom());
+ } else {
+ _vruler->set_range(rulerbox.bottom(), rulerbox.top());
+ }
+
+ Geom::Point pos(_canvas->get_pos());
+ auto scale = _canvas->get_affine();
+ auto d2c = Geom::Translate(pos * scale.inverse()).inverse() * scale;
+ auto pagebox = (pm.getSelectedPageRect() * d2c).roundOutwards();
+ _hruler->set_page(pagebox.left(), pagebox.right());
+ _vruler->set_page(pagebox.top(), pagebox.bottom());
+
+ Geom::Rect selbox = Geom::IntRect(0, 0, 0, 0);
+ if (auto bbox = sel->preferredBounds())
+ selbox = (*bbox * d2c).roundOutwards();
+ _hruler->set_selection(selbox.left(), selbox.right());
+ _vruler->set_selection(selbox.top(), selbox.bottom());
+}
+
+void
+CanvasGrid::ShowScrollbars(bool state)
+{
+ if (_show_scrollbars == state) return;
+ _show_scrollbars = state;
+
+ if (_show_scrollbars) {
+ // Show scrollbars
+ _hscrollbar.show();
+ _vscrollbar.show();
+ _cms_adjust.show();
+ _cms_adjust.show_all_children();
+ _quick_actions.show();
+ } else {
+ // Hide scrollbars
+ _hscrollbar.hide();
+ _vscrollbar.hide();
+ _cms_adjust.hide();
+ _quick_actions.hide();
+ }
+}
+
+void
+CanvasGrid::ToggleScrollbars()
+{
+ _show_scrollbars = !_show_scrollbars;
+ ShowScrollbars(_show_scrollbars);
+
+ // Will be replaced by actions
+ auto prefs = Inkscape::Preferences::get();
+ prefs->setBool("/fullscreen/scrollbars/state", _show_scrollbars);
+ prefs->setBool("/window/scrollbars/state", _show_scrollbars);
+}
+
+void
+CanvasGrid::ShowRulers(bool state)
+{
+ if (_show_rulers == state) return;
+ _show_rulers = state;
+
+ if (_show_rulers) {
+ // Show rulers
+ _hruler->show();
+ _vruler->show();
+ _guide_lock.show();
+ _guide_lock.show_all_children();
+ } else {
+ // Hide rulers
+ _hruler->hide();
+ _vruler->hide();
+ _guide_lock.hide();
+ }
+}
+
+void
+CanvasGrid::ToggleRulers()
+{
+ _show_rulers = !_show_rulers;
+ ShowRulers(_show_rulers);
+
+ // Will be replaced by actions
+ auto prefs = Inkscape::Preferences::get();
+ prefs->setBool("/fullscreen/rulers/state", _show_rulers);
+ prefs->setBool("/window/rulers/state", _show_rulers);
+}
+
+void
+CanvasGrid::ToggleCommandPalette()
+{
+ _command_palette->toggle();
+}
+
+void
+CanvasGrid::showNotice(Glib::ustring const &msg, unsigned timeout)
+{
+ _notice->show(msg, timeout);
+}
+
+void
+CanvasGrid::ShowCommandPalette(bool state)
+{
+ if (state) {
+ _command_palette->open();
+ }
+ _command_palette->close();
+}
+
+// Update rulers on change of widget size, but only if allocation really changed.
+void
+CanvasGrid::on_size_allocate(Gtk::Allocation& allocation)
+{
+ Gtk::Grid::on_size_allocate(allocation);
+ if (!(_allocation == allocation)) { // No != function defined!
+ _allocation = allocation;
+ UpdateRulers();
+ }
+}
+
+// This belong in Canvas class
+bool
+CanvasGrid::SignalEvent(GdkEvent *event)
+{
+ if (event->type == GDK_BUTTON_PRESS) {
+ _canvas->grab_focus();
+ _command_palette->close();
+ }
+
+ if (event->type == GDK_BUTTON_PRESS && event->button.button == 3) {
+ _dtw->desktop->getCanvasDrawing()->set_sticky(event->button.state & GDK_SHIFT_MASK);
+ }
+
+ // Pass keyboard events back to the desktop root handler so TextTool can work
+ if ((event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)
+ && !_canvas->get_current_canvas_item())
+ {
+ return sp_desktop_root_handler(event, _dtw->desktop);
+ }
+
+ return false;
+}
+
+// TODO Add actions so we can set shortcuts.
+// * Sticky Zoom
+// * CMS Adjust
+// * Guide Lock
+
+
+} // namespace Widget
+} // namespace UI
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :