// SPDX-License-Identifier: GPL-2.0-or-later #ifndef INKSCAPE_UI_DIALOG_MULTIPANED_H #define INKSCAPE_UI_DIALOG_MULTIPANED_H /** @file * @brief A widget with multiple panes. Agnostic to type what kind of widgets panes contain. * * Authors: see git history * Tavmjong Bah * * Copyright (c) 2020 Tavmjong Bah, Authors * * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #include #include #include #include #include #include namespace Inkscape { namespace UI { namespace Dialog { /** A widget with multiple panes */ class MyDropZone; class MyHandle; class DialogMultipaned; /* ============ DROPZONE ============ */ /** * Dropzones are eventboxes at the ends of a DialogMultipaned where you can drop dialogs. */ class MyDropZone : public Gtk::Orientable , public Gtk::EventBox { public: MyDropZone(Gtk::Orientation orientation); ~MyDropZone() override; static void add_highlight_instances(); static void remove_highlight_instances(); private: void set_size(int size); bool _active = false; void add_highlight(); void remove_highlight(); static std::list _instances_list; }; /* ============ HANDLE ============ */ /** * Handles are event boxes that help with resizing DialogMultipaned' children. */ class MyHandle : public Gtk::Orientable , public Gtk::EventBox { public: MyHandle(Gtk::Orientation orientation, int size); ~MyHandle() override = default; bool on_enter_notify_event(GdkEventCrossing *crossing_event) override; void set_dragging(bool dragging); private: bool on_leave_notify_event(GdkEventCrossing* crossing_event) override; bool on_button_press_event(GdkEventButton* button_event) override; bool on_button_release_event(GdkEventButton *event) override; bool on_motion_notify_event(GdkEventMotion* motion_event) override; void toggle_multipaned(); void update_click_indicator(double x, double y); void show_click_indicator(bool show); bool on_draw(const Cairo::RefPtr& cr) override; Cairo::Rectangle get_active_click_zone(); int _cross_size; Gtk::Widget *_child; void resize_handler(Gtk::Allocation &allocation); bool is_click_resize_active() const; bool _click = false; bool _click_indicator = false; bool _dragging = false; }; /* ============ MULTIPANE ============ */ /* * A widget with multiple panes. Agnostic to type what kind of widgets panes contain. * Handles allow a user to resize children widgets. Drop zones allow adding widgets * at either end. */ class DialogMultipaned : public Gtk::Orientable , public Gtk::Container { public: DialogMultipaned(Gtk::Orientation orientation = Gtk::ORIENTATION_HORIZONTAL); ~DialogMultipaned() override; void prepend(Gtk::Widget *new_widget); void append(Gtk::Widget *new_widget); // Getters and setters Gtk::Widget *get_first_widget(); Gtk::Widget *get_last_widget(); std::vector get_children() { return children; } void set_target_entries(const std::vector &target_entries); bool has_empty_widget() { return (bool)_empty_widget; } // Signals sigc::signal)> signal_prepend_drag_data(); sigc::signal)> signal_append_drag_data(); sigc::signal signal_now_empty(); // UI functions void set_dropzone_sizes(int start, int end); void toggle_multipaned_children(bool show); void children_toggled(); void ensure_multipaned_children(); void set_restored_width(int width); protected: // Overrides Gtk::SizeRequestMode get_request_mode_vfunc() const override; void get_preferred_width_vfunc(int &minimum_width, int &natural_width) const override; void get_preferred_height_vfunc(int &minimum_height, int &natural_height) const override; void get_preferred_width_for_height_vfunc(int height, int &minimum_width, int &natural_width) const override; void get_preferred_height_for_width_vfunc(int width, int &minimum_height, int &natural_height) const override; void on_size_allocate(Gtk::Allocation &allocation) override; // Allow us to keep track of our widgets ourselves. void forall_vfunc(gboolean include_internals, GtkCallback callback, gpointer callback_data) override; void on_add(Gtk::Widget *child) override; void on_remove(Gtk::Widget *child) override; // Signals sigc::signal)> _signal_prepend_drag_data; sigc::signal)> _signal_append_drag_data; sigc::signal _signal_now_empty; private: // We must manage children ourselves. std::vector children; // Values used when dragging handle. int _handle = -1; // Child number of active handle int _drag_handle = -1; Gtk::Widget* _resizing_widget1 = nullptr; Gtk::Widget* _resizing_widget2 = nullptr; Gtk::Widget* _hide_widget1 = nullptr; Gtk::Widget* _hide_widget2 = nullptr; Gtk::Allocation start_allocation1; Gtk::Allocation start_allocationh; Gtk::Allocation start_allocation2; Gtk::Allocation allocation1; Gtk::Allocation allocationh; Gtk::Allocation allocation2; Glib::RefPtr gesture; // Signal callbacks void on_drag_begin(double start_x, double start_y); void on_drag_end(double offset_x, double offset_y); void on_drag_update(double offset_x, double offset_y); void on_drag_data(const Glib::RefPtr context, int x, int y, const Gtk::SelectionData &selection_data, guint info, guint time); void on_prepend_drag_data(const Glib::RefPtr context, int x, int y, const Gtk::SelectionData &selection_data, guint info, guint time); void on_append_drag_data(const Glib::RefPtr context, int x, int y, const Gtk::SelectionData &selection_data, guint info, guint time); // Others Gtk::Widget *_empty_widget; // placeholder in an empty container void add_empty_widget(); void remove_empty_widget(); std::vector _connections; int _natural_width = 0; }; } // namespace Dialog } // namespace UI } // namespace Inkscape #endif // INKSCAPE_UI_DIALOG_MULTIPANED_H /* 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 :