Παράδειγμα παλέτας εργαλείου
Αυτό το παράδειγμα προσθέτει μια ToolPalette και μια DrawingArea σε ένα παράθυρο και επιτρέπει στον χρήστη να μεταφέρει εικονίδια από την παλέτα εργαλείων στην περιοχή σχεδίασης. Η παλέτα εργαλείων περιέχει αρκετές ομάδες στοιχείων. Τα σύνθετα πλαίσια επιτρέπουν στον χρήστη να αλλάξει την τεχνοτροπία και τον προσανατολισμό της παλέτας εργαλείων.
File: canvas.h (For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_EXAMPLE_CANVAS_H #define GTKMM_EXAMPLE_CANVAS_H #include <gtkmm.h> // This little canvas class is only here // because gtkmm does not have a canvas class yet. // Applications should probably use GooCanvas::Canvas (goocanvasmm) instead. class Canvas : public Gtk::DrawingArea { public: Canvas(); virtual ~Canvas(); private: class CanvasItem { public: CanvasItem(Gtk::Widget* canvas, Gtk::ToolButton* button, double x, double y) { Glib::ustring icon_name(button->get_icon_name()); if (icon_name.empty()) icon_name = button->get_label(); auto icon_theme = Gtk::IconTheme::get_for_screen(canvas->get_screen()); int width = 0; int height = 0; //ignored Gtk::IconSize::lookup(Gtk::ICON_SIZE_DIALOG, width, height); this->m_pixbuf = icon_theme->load_icon(icon_name, width, Gtk::ICON_LOOKUP_GENERIC_FALLBACK); this->m_x = x; this->m_y = y; } Glib::RefPtr<Gdk::Pixbuf> m_pixbuf; double m_x, m_y; }; void item_draw(const CanvasItem *item, const Cairo::RefPtr<Cairo::Context>& cr, bool preview); bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr) override; void on_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& selection_data, guint info, guint time) override; bool on_drag_motion(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint time) override; bool on_drag_drop(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint time) override; void on_drag_leave(const Glib::RefPtr<Gdk::DragContext>& context, guint time) override; bool m_drag_data_requested_for_drop; //So we know what to do in on_drag_data_received(). CanvasItem* m_drop_item; typedef std::vector<CanvasItem*> type_vec_items; type_vec_items m_canvas_items; }; #endif //GTKMM_EXAMPLE_CANVAS_H
File: examplewindow.h (For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_EXAMPLEWINDOW_H #define GTKMM_EXAMPLEWINDOW_H #include <gtkmm.h> #include "canvas.h" class ExampleWindow : public Gtk::Window { public: ExampleWindow(); virtual ~ExampleWindow(); private: void load_icon_items(); void load_toggle_items(); void load_special_items(); //Signal handlers: void on_combo_orientation_changed(); void on_combo_style_changed(); //Tree model columns: class ModelColumnsOrientation : public Gtk::TreeModel::ColumnRecord { public: ModelColumnsOrientation() { add(m_col_value); add(m_col_name); } Gtk::TreeModelColumn<Gtk::Orientation> m_col_value; Gtk::TreeModelColumn<Glib::ustring> m_col_name; }; ModelColumnsOrientation m_ColumnsOrientation; //Tree model columns: class ModelColumnsStyle : public Gtk::TreeModel::ColumnRecord { public: ModelColumnsStyle() { add(m_col_value); add(m_col_name); } Gtk::TreeModelColumn<int> m_col_value; //We use int to also allow -1 Gtk::TreeModelColumn<Glib::ustring> m_col_name; }; ModelColumnsStyle m_ColumnsStyle; //Child widgets: Gtk::Box m_VBox; Gtk::Box m_HBox; Gtk::ComboBox m_ComboOrientation; Glib::RefPtr<Gtk::ListStore> m_refTreeModelOrientation; Gtk::ComboBox m_ComboStyle; Glib::RefPtr<Gtk::ListStore> m_refTreeModelStyle; Gtk::ToolPalette m_ToolPalette; Gtk::ScrolledWindow m_ScrolledWindowPalette; Gtk::ScrolledWindow m_ScrolledWindowCanvas; Canvas m_Canvas; }; #endif //GTKMM_EXAMPLEWINDOW_H
File: examplewindow.cc (For use with gtkmm 3, not gtkmm 2)
#include "examplewindow.h" void ExampleWindow::load_icon_items() { auto icon_theme = Gtk::IconTheme::get_for_screen(get_screen()); typedef std::vector<Glib::ustring> type_stringvec; type_stringvec icon_names = icon_theme->list_icons(); // Obtain the names of all contexts, and the icon names per context. type_stringvec contexts = icon_theme->list_contexts(); std::sort(contexts.begin(), contexts.end()); int requested_icon_size = 0; int requested_icon_height = 0; //ignored Gtk::IconSize::lookup(Gtk::ICON_SIZE_BUTTON, requested_icon_size, requested_icon_height); const guint max_icons_per_group = 10; for (type_stringvec::const_iterator iter = contexts.begin(); iter != contexts.end(); ++iter) { const Glib::ustring context_name = *iter; Gtk::ToolItemGroup* group = Gtk::manage(new Gtk::ToolItemGroup(context_name)); m_ToolPalette.add(*group); // Iterate through the icon names, populating the ToolItemGroup as appropriate. type_stringvec icon_names_for_context = icon_theme->list_icons(context_name); std::sort(icon_names_for_context.begin(), icon_names_for_context.end()); guint icons_count = 0; for (type_stringvec::const_iterator iconiter = icon_names_for_context.begin(); iconiter != icon_names_for_context.end(); ++iconiter) { const Glib::ustring icon_name = *iconiter; Glib::RefPtr<Gdk::Pixbuf> pixbuf; try { pixbuf = icon_theme->load_icon(icon_name, requested_icon_size, Gtk::ICON_LOOKUP_GENERIC_FALLBACK); } catch (const Gtk::IconThemeError& /* ex */) { // Gtk::IconTheme::list_icons() may return some names of icons // that can't be loaded. continue; } // Skip large icons, just to make the ToolPalette look better. if (pixbuf->get_width() > 2*requested_icon_size || pixbuf->get_height() > 2*requested_icon_size) continue; auto image = Gtk::manage(new Gtk::Image(pixbuf)); auto button = Gtk::manage(new Gtk::ToolButton(*image, icon_name)); button->set_tooltip_text(icon_name); button->set_is_important(); group->insert(*button); // Prevent us having an insane number of icons: ++icons_count; if(icons_count >= max_icons_per_group) break; } } } void ExampleWindow::load_toggle_items() { auto group = Gtk::manage(new Gtk::ToolItemGroup("Radio Item")); m_ToolPalette.add(*group); Gtk::RadioToolButton::Group radio_group; for(int i = 1; i <= 10; ++i) { const Glib::ustring label = Glib::ustring::compose("#%1", i); auto button = Gtk::manage(new Gtk::RadioToolButton()); button->set_group(radio_group); button->set_label(label); group->insert(*button); } } static Gtk::ToolItem* create_entry_item(const Glib::ustring& text) { auto entry = Gtk::manage(new Gtk::Entry()); entry->set_text(text); entry->set_width_chars(5); auto item = Gtk::manage(new Gtk::ToolItem()); item->add(*entry); return item; } void ExampleWindow::load_special_items() { auto group = Gtk::manage(new Gtk::ToolItemGroup()); Gtk::Button *label_button = Gtk::manage(new Gtk::Button("Advanced Features")); label_button->show(); group->set_label_widget(*label_button); m_ToolPalette.add(*group); auto item = create_entry_item ("homogeneous=false"); group->insert(*item); //TODO: Add Gtk::Container::set_child_property(). gtk_container_child_set (GTK_CONTAINER (group->gobj()), GTK_WIDGET (item->gobj()), "homogeneous", FALSE, NULL); item = create_entry_item ("homogeneous=FALSE, expand=TRUE"); group->insert(*item); gtk_container_child_set (GTK_CONTAINER (group->gobj()), GTK_WIDGET (item->gobj()), "homogeneous", FALSE, "expand", TRUE, NULL); item = create_entry_item ("homogeneous=FALSE, expand=TRUE, fill=FALSE"); group->insert(*item); gtk_container_child_set (GTK_CONTAINER (group->gobj()), GTK_WIDGET (item->gobj()), "homogeneous", FALSE, "expand", TRUE, "fill", FALSE, NULL); item = create_entry_item ("homogeneous=FALSE, expand=TRUE, new-row=TRUE"); group->insert(*item); gtk_container_child_set (GTK_CONTAINER (group->gobj()), GTK_WIDGET (item->gobj()), "homogeneous", FALSE, "expand", TRUE, "new-row", TRUE, NULL); Gtk::ToolButton *button = Gtk::manage(new Gtk::ToolButton()); button->set_icon_name("go-up"); button->set_tooltip_text("Show on vertical palettes only"); group->insert(*button); button->set_visible_horizontal(false); button = Gtk::manage(new Gtk::ToolButton()); button->set_icon_name("go-next"); button->set_tooltip_text("Show on horizontal palettes only"); group->insert(*button); button->set_visible_vertical(false); button = Gtk::manage(new Gtk::ToolButton()); button->set_icon_name("edit-delete"); button->set_tooltip_text("Do not show at all"); button->set_no_show_all(); group->insert(*button); button->set_visible_vertical(false); button = Gtk::manage(new Gtk::ToolButton()); button->set_icon_name("view-fullscreen"); button->set_tooltip_text("Expanded this item"); group->insert(*button); gtk_container_child_set (GTK_CONTAINER (group->gobj()), GTK_WIDGET (button->gobj()), "homogeneous", FALSE, "expand", TRUE, NULL); button = Gtk::manage(new Gtk::ToolButton()); button->set_icon_name("help-contents"); button->set_tooltip_text("A regular item"); group->insert(*button); } ExampleWindow::ExampleWindow() : m_VBox(Gtk::ORIENTATION_VERTICAL, 6), m_HBox(Gtk::ORIENTATION_HORIZONTAL, 6) { set_title("Gtk::ToolPalette example"); set_size_request(600, 600); set_border_width(6); add(m_VBox); //The Orientation ComboBox: m_refTreeModelOrientation = Gtk::ListStore::create(m_ColumnsOrientation); Gtk::TreeModel::Row row = *(m_refTreeModelOrientation->append()); row[m_ColumnsOrientation.m_col_value] = Gtk::ORIENTATION_HORIZONTAL; row[m_ColumnsOrientation.m_col_name] = "Horizontal";\ row = *(m_refTreeModelOrientation->append()); row[m_ColumnsOrientation.m_col_value] = Gtk::ORIENTATION_VERTICAL; row[m_ColumnsOrientation.m_col_name] = "Vertical"; m_ComboOrientation.set_model(m_refTreeModelOrientation); m_VBox.pack_start(m_ComboOrientation, Gtk::PACK_SHRINK); m_ComboOrientation.pack_start(m_ColumnsOrientation.m_col_name); m_ComboOrientation.signal_changed().connect( sigc::mem_fun(*this, &ExampleWindow::on_combo_orientation_changed) ); m_ComboOrientation.set_active(row); //The Style ComboBox: m_refTreeModelStyle = Gtk::ListStore::create(m_ColumnsStyle); row = *(m_refTreeModelStyle->append()); row[m_ColumnsStyle.m_col_value] = Gtk::TOOLBAR_TEXT; row[m_ColumnsStyle.m_col_name] = "Text";\ row = *(m_refTreeModelStyle->append()); row[m_ColumnsStyle.m_col_value] = Gtk::TOOLBAR_BOTH; row[m_ColumnsStyle.m_col_name] = "Both"; row = *(m_refTreeModelStyle->append()); row[m_ColumnsStyle.m_col_value] = Gtk::TOOLBAR_BOTH_HORIZ; row[m_ColumnsStyle.m_col_name] = "Both: Horizontal"; row = *(m_refTreeModelStyle->append()); row[m_ColumnsStyle.m_col_value] = Gtk::TOOLBAR_ICONS; row[m_ColumnsStyle.m_col_name] = "Icons"; row = *(m_refTreeModelStyle->append()); row[m_ColumnsStyle.m_col_value] = -1; // A custom meaning for this demo. row[m_ColumnsStyle.m_col_name] = "Default"; m_ComboStyle.set_model(m_refTreeModelStyle); m_VBox.pack_start(m_ComboStyle, Gtk::PACK_SHRINK); m_ComboStyle.pack_start(m_ColumnsStyle.m_col_name); m_ComboStyle.signal_changed().connect( sigc::mem_fun(*this, &ExampleWindow::on_combo_style_changed) ); m_ComboStyle.set_active(row); //Add and fill the ToolPalette: load_icon_items(); load_toggle_items(); load_special_items(); m_VBox.pack_start(m_HBox, Gtk::PACK_EXPAND_WIDGET); m_ScrolledWindowPalette.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); m_ScrolledWindowPalette.set_border_width(6); m_ScrolledWindowPalette.add(m_ToolPalette); m_HBox.pack_start(m_ScrolledWindowPalette); on_combo_orientation_changed(); m_ScrolledWindowCanvas.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS); m_ScrolledWindowCanvas.set_border_width(6); m_ScrolledWindowCanvas.add(m_Canvas); m_ScrolledWindowCanvas.set_size_request(200, -1); m_HBox.pack_start(m_ScrolledWindowCanvas); m_ToolPalette.add_drag_dest(m_Canvas, Gtk::DEST_DEFAULT_HIGHLIGHT, Gtk::TOOL_PALETTE_DRAG_ITEMS, Gdk::ACTION_COPY); show_all_children(); } ExampleWindow::~ExampleWindow() { } void ExampleWindow::on_combo_orientation_changed() { Gtk::TreeModel::iterator iter = m_ComboOrientation.get_active(); if(!iter) return; Gtk::TreeModel::Row row = *iter; const Gtk::Orientation value = row[m_ColumnsOrientation.m_col_value]; m_ToolPalette.set_orientation(value); if(value == Gtk::ORIENTATION_HORIZONTAL) m_ScrolledWindowPalette.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_NEVER); else m_ScrolledWindowPalette.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); } void ExampleWindow::on_combo_style_changed() { Gtk::TreeModel::iterator iter = m_ComboStyle.get_active(); if(!iter) return; Gtk::TreeModel::Row row = *iter; const int value = row[m_ColumnsStyle.m_col_value]; if(value == -1) m_ToolPalette.unset_style(); else m_ToolPalette.set_style((Gtk::ToolbarStyle)value); }
File: canvas.cc (For use with gtkmm 3, not gtkmm 2)
#include "canvas.h" #include <iostream> Canvas::Canvas() : m_drag_data_requested_for_drop(false), m_drop_item(0) { set_app_paintable(); } Canvas::~Canvas() { while(!m_canvas_items.empty()) { auto iter = m_canvas_items.begin(); auto item = *iter; delete item; m_canvas_items.erase(iter); } delete m_drop_item; } void Canvas::item_draw(const CanvasItem *item, const Cairo::RefPtr<Cairo::Context>& cr, bool preview) { if(!item || !item->m_pixbuf) return; const double cx = item->m_pixbuf->get_width(); const double cy = item->m_pixbuf->get_height(); Gdk::Cairo::set_source_pixbuf(cr, item->m_pixbuf, item->m_x - cx * 0.5, item->m_y - cy * 0.5); if(preview) cr->paint_with_alpha(0.6); else cr->paint(); } bool Canvas::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) { cr->set_source_rgb(1.0, 1.0, 1.0); const Gtk::Allocation allocation = get_allocation(); cr->rectangle(0, 0, allocation.get_width(), allocation.get_height()); cr->fill(); for(type_vec_items::iterator iter = m_canvas_items.begin(); iter != m_canvas_items.end(); ++iter ) { item_draw(*iter, cr, false); } if(m_drop_item) item_draw (m_drop_item, cr, true); return true; } bool Canvas::on_drag_motion(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint time) { m_drag_data_requested_for_drop = false; //It's for drag-motion instead. if(m_drop_item) { // We already have a drop indicator so just update its position. m_drop_item->m_x = x; m_drop_item->m_y = y; queue_draw(); context->drag_status(Gdk::ACTION_COPY, time); } else { // Request DnD data for creating a drop indicator. // This will cause on_drag_data_received() to be called. const Glib::ustring target = drag_dest_find_target(context); if (target.empty()) return false; drag_get_data(context, target, time); } Gtk::DrawingArea::on_drag_motion(context, x, y, time); return true; } void Canvas::on_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& selection_data, guint info, guint time) { // Find the tool button which is the source of this DnD operation. auto widget = drag_get_source_widget(context); auto drag_palette = dynamic_cast<Gtk::ToolPalette*>(widget); while(widget && !drag_palette) { widget = widget->get_parent(); drag_palette = dynamic_cast<Gtk::ToolPalette*>(widget); } Gtk::ToolItem* drag_item = nullptr; if(drag_palette) drag_item = drag_palette->get_drag_item(selection_data); // Create a drop indicator when a tool button was found: auto button = dynamic_cast<Gtk::ToolButton*>(drag_item); if(!button) return; delete m_drop_item; m_drop_item = nullptr; try { auto item = new CanvasItem(this, button, x, y); if(m_drag_data_requested_for_drop) { m_canvas_items.push_back(item); // Signal that the item was accepted and then redraw. context->drag_finish(true /* success */, false /* del */, time); } else { m_drop_item = item; // We are getting this data due to a request in drag_motion, // rather than due to a request in drag_drop, so we are just // supposed to call gdk_drag_status (), not actually paste in // the data. context->drag_status(Gdk::ACTION_COPY, time); } queue_draw(); } catch (const Gtk::IconThemeError& ex) { std::cerr << "IconThemeError: " << ex.what() << std::endl; } Gtk::DrawingArea::on_drag_data_received(context, x, y, selection_data, info, time); } bool Canvas::on_drag_drop(const Glib::RefPtr<Gdk::DragContext>& context, int /* x */, int /* y */, guint time) { // Request DnD data for creating a dopped item. // This will cause on_drag_data_received() to be called. const Glib::ustring target = drag_dest_find_target(context); if (target.empty()) return false; m_drag_data_requested_for_drop = true; drag_get_data(context, target, time); return true; } void Canvas::on_drag_leave(const Glib::RefPtr<Gdk::DragContext>& context, guint time) { //This signal is emitted to clean up the item used for drag-motion, //either when the cursor moves out of the widget or when we drop. if(!m_drop_item) return; delete m_drop_item; m_drop_item = nullptr; queue_draw(); Gtk::DrawingArea::on_drag_leave(context, time); }
File: main.cc (For use with gtkmm 3, not gtkmm 2)
#include "examplewindow.h" #include <gtkmm/application.h> int main(int argc, char *argv[]) { auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example"); ExampleWindow window; //Shows the window and returns when it is closed. return app->run(window); }