Exemple de palette d'outils
Dans cet exemple, des objets ToolPalette et DrawingArea sont associés à une fenêtre. L'utilisateur est autorisé à faire un glisser-déposer depuis la palette d'outils sur la surface de tracé. La palette d'outils contient divers groupes d'éléments. Des boîtes combinées permettent à l'utilisateur de changer le style et l'orientation de la palette d'outils.
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) { const Gtk::StockID stock_id(button->get_stock_id()); this->pixbuf = canvas->render_icon_pixbuf(stock_id, Gtk::ICON_SIZE_DIALOG); this->x = x; this->y = y; } Glib::RefPtr<Gdk::Pixbuf> pixbuf; double x, y; }; void item_draw(const CanvasItem *item, const Cairo::RefPtr<Cairo::Context>& cr, bool preview); virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr); virtual void on_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& selection_data, guint info, guint time); virtual bool on_drag_motion(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint time); virtual bool on_drag_drop(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint time); virtual void on_drag_leave(const Glib::RefPtr<Gdk::DragContext>& context, guint time); 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_stock_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" static bool sort_predicate(const Gtk::StockID& a, const Gtk::StockID& b) { return a.get_string() < b.get_string(); } void ExampleWindow::load_stock_items() { Gtk::ToolItemGroup* group_af = Gtk::manage(new Gtk::ToolItemGroup("Stock Icons (A-F)")); m_ToolPalette.add(*group_af); Gtk::ToolItemGroup* group_gn = Gtk::manage(new Gtk::ToolItemGroup("Stock Icons (G-N)")); m_ToolPalette.add(*group_gn); Gtk::ToolItemGroup* group_or = Gtk::manage(new Gtk::ToolItemGroup("Stock Icons (O-R)")); m_ToolPalette.add(*group_or); Gtk::ToolItemGroup* group_sz = Gtk::manage(new Gtk::ToolItemGroup("Stock Icons (S-Z)")); m_ToolPalette.add(*group_sz); // Obtain the IDs of all stock items: typedef std::vector<Gtk::StockID> type_vecIDs; type_vecIDs vecIDs = Gtk::Stock::get_ids(); std::sort(vecIDs.begin(), vecIDs.end(), &sort_predicate); Gtk::ToolItemGroup* group = 0; // Iterate through them, populating the ListStore as appropriate: for(type_vecIDs::const_iterator iterIDs = vecIDs.begin(); iterIDs != vecIDs.end(); ++iterIDs) { const Gtk::StockID& stockid = *iterIDs; const Glib::ustring str = stockid.get_string(); if(str.size() < 4) continue; switch(str[4]) { case 'a': group = group_af; break; case 'g': group = group_gn; break; case 'o': group = group_or; break; case 's': group = group_sz; break; default: //Use the previous group //(They are sorted.) break; } if(!group) continue; Gtk::ToolButton* button = Gtk::manage(new Gtk::ToolButton(stockid)); button->set_tooltip_text(str); button->set_is_important(); group->insert(*button); Gtk::StockItem stockitem; if(!Gtk::StockItem::lookup(stockid, stockitem) || stockitem.get_label().empty()) button->set_label(str); } } void ExampleWindow::load_toggle_items() { Gtk::ToolItemGroup* 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); Gtk::RadioToolButton* 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) { Gtk::Entry* entry = Gtk::manage(new Gtk::Entry()); entry->set_text(text); entry->set_width_chars(5); Gtk::ToolItem* item = Gtk::manage(new Gtk::ToolItem()); item->add(*entry); return item; } void ExampleWindow::load_special_items() { Gtk::ToolItemGroup* 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); Gtk::ToolItem* 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); item = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::GO_UP)); item->set_tooltip_text("Show on vertical palettes only"); group->insert(*item); item->set_visible_horizontal(false); item = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::GO_FORWARD)); item->set_tooltip_text("Show on horizontal palettes only"); group->insert(*item); item->set_visible_vertical(false); item = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::FULLSCREEN)); item->set_tooltip_text("Expanded this item"); group->insert(*item); gtk_container_child_set (GTK_CONTAINER (group->gobj()), GTK_WIDGET (item->gobj()), "homogeneous", FALSE, "expand", TRUE, NULL); item = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::HELP)); item->set_tooltip_text("A regular item"); group->insert(*item); } 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_stock_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() { set_app_paintable(); } Canvas::~Canvas() { while(!m_canvas_items.empty()) { type_vec_items::iterator iter = m_canvas_items.begin(); CanvasItem* item = *iter; delete item; m_canvas_items.erase(iter); } if(m_drop_item) delete m_drop_item; } void Canvas::item_draw(const CanvasItem *item, const Cairo::RefPtr<Cairo::Context>& cr, bool preview) { if(!item || !item->pixbuf) return; const double cx = item->pixbuf->get_width(); const double cy = item->pixbuf->get_height(); Gdk::Cairo::set_source_pixbuf(cr, item->pixbuf, item->x - cx * 0.5, item->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->x = x; m_drop_item->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. Gtk::Widget* widget = drag_get_source_widget(context); Gtk::ToolPalette* 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 = 0; if(drag_palette) drag_item = drag_palette->get_drag_item(selection_data); // Create a drop indicator when a tool button was found: Gtk::ToolButton* button = dynamic_cast<Gtk::ToolButton*>(drag_item); if(!button) return; if(m_drop_item) { delete m_drop_item; m_drop_item = 0; } CanvasItem* 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(); 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 = 0; 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[]) { Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example"); ExampleWindow window; //Shows the window and returns when it is closed. return app->run(window); }