// Copyright (C) 2005 Davis E. King (davis@dlib.net), Keita Mochizuki // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_WIDGETs_ #define DLIB_WIDGETs_ #include <cctype> #include <memory> #include <set> #include <sstream> #include <string> #include <vector> #include "../algs.h" #include "widgets_abstract.h" #include "drawable.h" #include "../gui_core.h" #include "fonts.h" #include "../timer.h" #include "base_widgets.h" #include "../member_function_pointer.h" #include "../array.h" #include "../array2d.h" #include "../sequence.h" #include "../dir_nav.h" #include "../queue.h" #include "style.h" #include "../string.h" #include "../misc_api.h" #include "../any.h" #include "../image_processing/full_object_detection.h" #ifdef _MSC_VER // This #pragma directive is also located in the algs.h file but for whatever // reason visual studio 9 just ignores it when it is only there. // this is to disable the "'this' : used in base member initializer list" // warning you get from some of the GUI objects since all the objects // require that their parent class be passed into their constructor. // In this case though it is totally safe so it is ok to disable this warning. #pragma warning(disable : 4355) #endif namespace dlib { // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class label // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class label : public drawable { public: label( drawable_window& w ) : drawable(w), text_color_(0,0,0) { enable_events(); } ~label() { disable_events(); parent.invalidate_rectangle(rect); } void set_text ( const std::string& text ); void set_text ( const std::wstring& text ); void set_text ( const dlib::ustring& text ); const std::string text ( ) const; const std::wstring wtext ( ) const; const dlib::ustring utext ( ) const; void set_text_color ( const rgb_pixel color ); const rgb_pixel text_color ( ) const; void set_main_font ( const std::shared_ptr<font>& f ); private: dlib::ustring text_; rgb_pixel text_color_; // restricted functions label(label&); // copy constructor label& operator=(label&); // assignment operator protected: void draw ( const canvas& c ) const; }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class toggle_button // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class toggle_button : public button_action { /*! INITIAL VALUE - checked == false CONVENTION - is_checked() == checked !*/ public: toggle_button( drawable_window& w ) : button_action(w), btn_tooltip(w), checked(false) { style.reset(new toggle_button_style_default()); enable_events(); } ~toggle_button() { disable_events(); parent.invalidate_rectangle(rect); } void set_name ( const std::string& name ); void set_name ( const std::wstring& name ); void set_name ( const dlib::ustring& name ); void set_size ( unsigned long width_, unsigned long height_ ); void set_tooltip_text ( const std::string& text ); void set_tooltip_text ( const std::wstring& text ); void set_tooltip_text ( const ustring& text ); const std::string tooltip_text ( ) const; const std::wstring tooltip_wtext ( ) const; const dlib::ustring tooltip_utext ( ) const; bool is_checked ( ) const; const std::string name ( ) const; const std::wstring wname ( ) const; const dlib::ustring uname ( ) const; void set_checked ( ); void set_unchecked ( ); void show ( ); void hide ( ); void enable ( ); void disable ( ); void set_main_font ( const std::shared_ptr<font>& f ); void set_pos ( long x, long y ); template < typename style_type > void set_style ( const style_type& style_ ) { auto_mutex M(m); style.reset(new style_type(style_)); rect = move_rect(style->get_min_size(name_,*mfont), rect.left(), rect.top()); parent.invalidate_rectangle(rect); } template < typename T > void set_click_handler ( T& object, void (T::*event_handler_)() ) { auto_mutex M(m); event_handler = make_mfp(object,event_handler_); event_handler_self.clear(); } void set_click_handler ( const any_function<void()>& event_handler_ ) { auto_mutex M(m); event_handler = event_handler_; event_handler_self.clear(); } template < typename T > void set_click_handler ( T& object, void (T::*event_handler_)(toggle_button&) ) { auto_mutex M(m); event_handler_self = make_mfp(object,event_handler_); event_handler.clear(); } void set_sourced_click_handler ( const any_function<void(toggle_button&)>& event_handler_ ) { auto_mutex M(m); event_handler_self = event_handler_; event_handler.clear(); } private: // restricted functions toggle_button(toggle_button&); // copy constructor toggle_button& operator=(toggle_button&); // assignment operator dlib::ustring name_; tooltip btn_tooltip; bool checked; any_function<void()> event_handler; any_function<void(toggle_button&)> event_handler_self; std::unique_ptr<toggle_button_style> style; protected: void draw ( const canvas& c ) const { style->draw_toggle_button(c,rect,enabled,*mfont,lastx,lasty,name_,is_depressed(),checked); } void on_button_up ( bool mouse_over ); void on_mouse_over ( ){ if (style->redraw_on_mouse_over()) parent.invalidate_rectangle(rect); } void on_mouse_not_over ( ){ if (style->redraw_on_mouse_over()) parent.invalidate_rectangle(rect); } }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class text_field // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class text_field : public drawable { /*! INITIAL VALUE text_color_ == rgb_pixel(0,0,0) bg_color_ == rgb_pixel(255,255,255) cursor_pos == 0 text_width == 0 text_ == "" has_focus == false cursor_visible == false recent_movement == false highlight_start == 0 highlight_end == -1 shift_pos == -1 text_pos == 0 CONVENTION - cursor_pos == the position of the cursor in the string text_. The cursor appears before the letter text_[cursor_pos] - cursor_x == the x coordinate of the cursor relative to the left side of rect. i.e. the number of pixels that separate the cursor from the left side of the text_field. - has_focus == true if this text field has keyboard input focus - cursor_visible == true if the cursor should be painted - text_ == text() - text_pos == the index of the first letter in text_ that appears in this text field. - text_width == the width of text_[text_pos] though text_[text.size()-1] - if (has_focus && the user has recently moved the cursor) then - recent_movement == true - else - recent_movement == false - if (highlight_start <= highlight_end) then - text[highlight_start] though text[highlight_end] should be highlighted - if (shift_pos != -1) then - has_focus == true - the shift key is being held down or the left mouse button is being held down. - shift_pos == the position of the cursor when the shift or mouse key was first pressed. - text_color() == text_color_ - background_color() == bg_color_ !*/ public: text_field( drawable_window& w ) : drawable(w,MOUSE_CLICK | KEYBOARD_EVENTS | MOUSE_MOVE | STRING_PUT), text_color_(0,0,0), bg_color_(255,255,255), text_width(0), text_pos(0), recent_movement(false), has_focus(false), cursor_visible(false), cursor_pos(0), highlight_start(0), highlight_end(-1), shift_pos(-1), t(*this,&text_field::timer_action), right_click_menu(w) { style.reset(new text_field_style_default()); rect.set_bottom(mfont->height()+ (style->get_padding(*mfont))*2); rect.set_right((style->get_padding(*mfont))*2); cursor_x = style->get_padding(*mfont); right_click_menu.menu().add_menu_item(menu_item_text("Cut",*this,&text_field::on_cut,'t')); right_click_menu.menu().add_menu_item(menu_item_text("Copy",*this,&text_field::on_copy,'C')); right_click_menu.menu().add_menu_item(menu_item_text("Paste",*this,&text_field::on_paste,'P')); right_click_menu.menu().add_menu_item(menu_item_text("Delete",*this,&text_field::on_delete_selected,'D')); right_click_menu.menu().add_menu_item(menu_item_separator()); right_click_menu.menu().add_menu_item(menu_item_text("Select All",*this,&text_field::on_select_all,'A')); right_click_menu.set_rect(get_text_rect()); enable_events(); t.set_delay_time(500); } ~text_field ( ) { disable_events(); parent.invalidate_rectangle(rect); t.stop_and_wait(); } template < typename style_type > void set_style ( const style_type& style_ ) { auto_mutex M(m); style.reset(new style_type(style_)); // call this just so that this widget redraws itself with the new style set_main_font(mfont); } void set_text ( const std::string& text_ ); void set_text ( const std::wstring& text_ ); void give_input_focus ( ); bool has_input_focus ( ) const; void select_all_text ( ); void set_text ( const dlib::ustring& text_ ); const std::string text ( ) const; const std::wstring wtext ( ) const; const dlib::ustring utext ( ) const; void set_text_color ( const rgb_pixel color ); const rgb_pixel text_color ( ) const; void set_background_color ( const rgb_pixel color ); const rgb_pixel background_color ( ) const; void set_width ( unsigned long width ); void set_pos ( long x, long y ); void set_main_font ( const std::shared_ptr<font>& f ); int next_free_user_event_number ( ) const { return drawable::next_free_user_event_number()+1; } void disable ( ); void enable ( ); void hide ( ); void show ( ); template < typename T > void set_text_modified_handler ( T& object, void (T::*event_handler)() ) { auto_mutex M(m); text_modified_handler = make_mfp(object,event_handler); } template < typename T > void set_enter_key_handler ( T& object, void (T::*event_handler)() ) { auto_mutex M(m); enter_key_handler = make_mfp(object,event_handler); } void set_text_modified_handler ( const any_function<void()>& event_handler ) { auto_mutex M(m); text_modified_handler = event_handler; } void set_enter_key_handler ( const any_function<void()>& event_handler ) { auto_mutex M(m); enter_key_handler = event_handler; } template < typename T > void set_focus_lost_handler ( T& object, void (T::*event_handler)() ) { auto_mutex M(m); focus_lost_handler = make_mfp(object,event_handler); } void set_focus_lost_handler ( const any_function<void()>& event_handler ) { auto_mutex M(m); focus_lost_handler = event_handler; } private: void on_cut ( ); void on_copy ( ); void on_paste ( ); void on_select_all ( ); void on_delete_selected ( ); void on_text_is_selected ( ); void on_no_text_selected ( ); void on_user_event ( int num ) { // ignore this user event if it isn't for us if (num != drawable::next_free_user_event_number()) return; if (recent_movement == false) { cursor_visible = !cursor_visible; parent.invalidate_rectangle(rect); } else { if (cursor_visible == false) { cursor_visible = true; parent.invalidate_rectangle(rect); } recent_movement = false; } } void timer_action ( ) { parent.trigger_user_event(this,drawable::next_free_user_event_number()); } /*! ensures - flips the state of cursor_visible !*/ void move_cursor ( unsigned long pos ); /*! requires - pos <= text_.size() ensures - moves the cursor to the position given by pos and moves the text in the text box if necessary - if the position changes then the parent window will be updated !*/ rectangle get_text_rect ( ) const; /*! ensures - returns the rectangle that should contain the text in this widget !*/ dlib::ustring text_; rgb_pixel text_color_; rgb_pixel bg_color_; unsigned long text_width; unsigned long text_pos; bool recent_movement; bool has_focus; bool cursor_visible; long cursor_pos; unsigned long cursor_x; // this tells you what part of the text is highlighted long highlight_start; long highlight_end; long shift_pos; any_function<void()> text_modified_handler; any_function<void()> enter_key_handler; any_function<void()> focus_lost_handler; std::unique_ptr<text_field_style> style; timer<text_field> t; popup_menu_region right_click_menu; // restricted functions text_field(text_field&); // copy constructor text_field& operator=(text_field&); // assignment operator protected: void draw ( const canvas& c ) const; void on_mouse_down ( unsigned long btn, unsigned long state, long x, long y, bool is_double_click ); void on_mouse_up ( unsigned long btn, unsigned long state, long x, long y ); void on_mouse_move ( unsigned long state, long x, long y ); void on_keydown ( unsigned long key, bool is_printable, unsigned long state ); void on_string_put ( const std::wstring &str ); }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class text_box // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class text_box : public scrollable_region { /*! INITIAL VALUE text_color_ == rgb_pixel(0,0,0) bg_color_ == rgb_pixel(255,255,255) cursor_pos == 0 text_ == "" has_focus == false cursor_visible == false recent_movement == false highlight_start == 0 highlight_end == -1 shift_pos == -1 CONVENTION - cursor_pos == the position of the cursor in the string text_. The cursor appears before the letter text_[cursor_pos] - cursor_rect == The rectangle that should be drawn for the cursor. The position is relative to total_rect(). - has_focus == true if this text field has keyboard input focus - cursor_visible == true if the cursor should be painted - text_ == text() - if (has_focus && the user has recently moved the cursor) then - recent_movement == true - else - recent_movement == false - if (highlight_start <= highlight_end) then - text[highlight_start] though text[highlight_end] should be highlighted - if (shift_pos != -1) then - has_focus == true - the shift key is being held down or the left mouse button is being held down. - shift_pos == the position of the cursor when the shift or mouse key was first pressed. - text_color() == text_color_ - background_color() == bg_color_ !*/ public: text_box( drawable_window& w ) : scrollable_region(w,MOUSE_CLICK | KEYBOARD_EVENTS | MOUSE_MOVE | STRING_PUT), text_color_(0,0,0), bg_color_(255,255,255), recent_movement(false), has_focus(false), cursor_visible(false), cursor_pos(0), highlight_start(0), highlight_end(-1), shift_pos(-1), t(*this,&text_box::timer_action), right_click_menu(w) { style.reset(new text_box_style_default()); const long padding = static_cast<long>(style->get_padding(*mfont)); cursor_rect = mfont->compute_cursor_rect(rectangle(padding,padding,1000000,1000000), text_, 0); adjust_total_rect(); set_vertical_mouse_wheel_scroll_increment(mfont->height()); set_horizontal_mouse_wheel_scroll_increment(mfont->height()); right_click_menu.menu().add_menu_item(menu_item_text("Cut",*this,&text_box::on_cut,'t')); right_click_menu.menu().add_menu_item(menu_item_text("Copy",*this,&text_box::on_copy,'C')); right_click_menu.menu().add_menu_item(menu_item_text("Paste",*this,&text_box::on_paste,'P')); right_click_menu.menu().add_menu_item(menu_item_text("Delete",*this,&text_box::on_delete_selected,'D')); right_click_menu.menu().add_menu_item(menu_item_separator()); right_click_menu.menu().add_menu_item(menu_item_text("Select All",*this,&text_box::on_select_all,'A')); right_click_menu.set_rect(get_text_rect()); set_size(100,100); enable_events(); t.set_delay_time(500); } ~text_box ( ) { disable_events(); parent.invalidate_rectangle(rect); t.stop_and_wait(); } template < typename style_type > void set_style ( const style_type& style_ ) { auto_mutex M(m); style.reset(new style_type(style_)); scrollable_region::set_style(style_.get_scrollable_region_style()); // call this just so that this widget redraws itself with the new style set_main_font(mfont); } void set_text ( const std::string& text_ ); void set_text ( const std::wstring& text_ ); void set_text ( const dlib::ustring& text_ ); const std::string text ( ) const; const std::wstring wtext ( ) const; const dlib::ustring utext ( ) const; void set_text_color ( const rgb_pixel color ); const rgb_pixel text_color ( ) const; void set_background_color ( const rgb_pixel color ); const rgb_pixel background_color ( ) const; void set_size ( unsigned long width, unsigned long height ); void set_pos ( long x, long y ); void set_main_font ( const std::shared_ptr<font>& f ); int next_free_user_event_number ( ) const { return scrollable_region::next_free_user_event_number()+1; } void disable ( ); void enable ( ); void hide ( ); void show ( ); template < typename T > void set_text_modified_handler ( T& object, void (T::*event_handler)() ) { auto_mutex M(m); text_modified_handler = make_mfp(object,event_handler); } void set_text_modified_handler ( const any_function<void()>& event_handler ) { auto_mutex M(m); text_modified_handler = event_handler; } template < typename T > void set_enter_key_handler ( T& object, void (T::*event_handler)() ) { auto_mutex M(m); enter_key_handler = make_mfp(object,event_handler); } void set_enter_key_handler ( const any_function<void()>& event_handler ) { auto_mutex M(m); enter_key_handler = event_handler; } template < typename T > void set_focus_lost_handler ( T& object, void (T::*event_handler)() ) { auto_mutex M(m); focus_lost_handler = make_mfp(object,event_handler); } void set_focus_lost_handler ( const any_function<void()>& event_handler ) { auto_mutex M(m); focus_lost_handler = event_handler; } private: void on_cut ( ); void on_copy ( ); void on_paste ( ); void on_select_all ( ); void on_delete_selected ( ); void on_text_is_selected ( ); void on_no_text_selected ( ); void on_user_event ( int num ) { // ignore this user event if it isn't for us if (num != scrollable_region::next_free_user_event_number()) return; if (recent_movement == false) { cursor_visible = !cursor_visible; parent.invalidate_rectangle(rect); } else { if (cursor_visible == false) { cursor_visible = true; parent.invalidate_rectangle(rect); } recent_movement = false; } } // The reason for using user actions here rather than just having the timer just call // what it needs directly is to avoid a potential deadlock during destruction of this widget. void timer_action ( ) { parent.trigger_user_event(this,scrollable_region::next_free_user_event_number()); } /*! ensures - flips the state of cursor_visible !*/ void move_cursor ( unsigned long pos ); /*! requires - pos <= text_.size() ensures - moves the cursor to the position given by pos and moves the text in the text box if necessary - if the position changes then the parent window will be updated !*/ rectangle get_text_rect ( ) const; /*! ensures - returns the rectangle that should contain the text in this widget !*/ void adjust_total_rect ( ); /*! ensures - adjusts total_rect() so that it is big enough to contain the text currently in this object. !*/ dlib::ustring text_; rgb_pixel text_color_; rgb_pixel bg_color_; bool recent_movement; bool has_focus; bool cursor_visible; long cursor_pos; rectangle cursor_rect; // this tells you what part of the text is highlighted long highlight_start; long highlight_end; long shift_pos; any_function<void()> text_modified_handler; any_function<void()> enter_key_handler; any_function<void()> focus_lost_handler; std::unique_ptr<text_box_style> style; timer<text_box> t; popup_menu_region right_click_menu; // restricted functions text_box(text_box&); // copy constructor text_box& operator=(text_box&); // assignment operator protected: void draw ( const canvas& c ) const; void on_mouse_down ( unsigned long btn, unsigned long state, long x, long y, bool is_double_click ); void on_mouse_up ( unsigned long btn, unsigned long state, long x, long y ); void on_mouse_move ( unsigned long state, long x, long y ); void on_keydown ( unsigned long key, bool is_printable, unsigned long state ); void on_string_put ( const std::wstring &str ); }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class check_box // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class check_box : public toggle_button { public: check_box( drawable_window& w ) : toggle_button(w) { set_style(toggle_button_style_check_box()); } }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class radio_button // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class radio_button : public toggle_button { public: radio_button ( drawable_window& w ) : toggle_button(w) { set_style(toggle_button_style_radio_button()); } }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class tabbed_display // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class tabbed_display : public drawable { /*! INITIAL VALUE - tabs.size() == 0 - selected_tab_ == 0 CONVENTION - number_of_tabs() == tabs.size() - tab_name(idx) == tabs[idx] - if (tabs.size() > 0) then - selected_tab_ == the index of the tab that is currently selected - for all valid i: - tabs[i].width == mfont->compute_size(tabs[i].name) - tabs[i].rect == the rectangle that defines where this tab is - if (tabs[i].group != 0) then - tabs[i].group == a pointer to the widget_group for this tab. - left_pad == the amount of padding in a tab to the left of the name string. - right_pad == the amount of padding in a tab to the right of the name string. - top_pad == the amount of padding in a tab to the top of the name string. - bottom_pad == the amount of padding in a tab to the bottom of the name string. - if (event_handler.is_set()) then - event_handler() is what is called to process click events on this object. !*/ public: tabbed_display( drawable_window& w ); virtual ~tabbed_display( ); void set_size ( unsigned long width, unsigned long height ); void set_number_of_tabs ( unsigned long num ); unsigned long number_of_tabs ( ) const; const std::string tab_name ( unsigned long idx ) const; const std::wstring tab_wname ( unsigned long idx ) const; const dlib::ustring& tab_uname ( unsigned long idx ) const; void set_tab_name ( unsigned long idx, const std::string& new_name ); void set_tab_name ( unsigned long idx, const std::wstring& new_name ); void set_tab_name ( unsigned long idx, const dlib::ustring& new_name ); void set_pos ( long x, long y ); template < typename T > void set_click_handler ( T& object, void (T::*eh)(unsigned long new_idx,unsigned long old_idx) ) { auto_mutex M(m); event_handler = make_mfp(object,eh); } void set_click_handler ( const any_function<void(unsigned long,unsigned long)>& eh ) { auto_mutex M(m); event_handler = eh; } void set_tab_group ( unsigned long idx, widget_group& group ); void show ( ); void hide ( ); void enable ( ); void disable ( ); void set_main_font ( const std::shared_ptr<font>& f ); void fit_to_contents ( ); protected: void on_mouse_down ( unsigned long btn, unsigned long state, long x, long y, bool is_double_click ); void draw ( const canvas& c ) const; private: void recompute_tabs ( ); /*! ensures - recomputes the rectangles for all the tabs and makes this object wider if needed !*/ void draw_tab ( const rectangle& tab, const canvas& c ) const; /*! ensures - draws the outline of a tab as given by the rectangle onto c !*/ struct tab_data { tab_data() : width(0), group(0) {} dlib::ustring name; unsigned long width; rectangle rect; widget_group* group; }; unsigned long selected_tab_; array<tab_data> tabs; const long left_pad; const long right_pad; const long top_pad; const long bottom_pad; any_function<void(unsigned long,unsigned long)> event_handler; // restricted functions tabbed_display(tabbed_display&); // copy constructor tabbed_display& operator=(tabbed_display&); // assignment operator }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class named_rectangle // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class named_rectangle : public drawable { /*! INITIAL VALUE name == "" CONVENTION name_ == name() !*/ public: named_rectangle( drawable_window& w ); virtual ~named_rectangle( ); void set_size ( unsigned long width, unsigned long height ); void set_name ( const std::string& name ); void set_name ( const std::wstring& name ); void set_name ( const dlib::ustring& name ); const std::string name ( ) const; const std::wstring wname ( ) const; const dlib::ustring uname ( ) const; void wrap_around ( const rectangle& rect ); void set_main_font ( const std::shared_ptr<font>& f ); protected: void draw ( const canvas& c ) const; private: void make_name_fit_in_rect ( ); dlib::ustring name_; unsigned long name_width; unsigned long name_height; // restricted functions named_rectangle(named_rectangle&); // copy constructor named_rectangle& operator=(named_rectangle&); // assignment operator }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class mouse_tracker // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class mouse_tracker : public draggable { public: mouse_tracker( drawable_window& w ); ~mouse_tracker( ); void show ( ); void hide ( ); void enable ( ); void disable ( ); void set_pos ( long x, long y ); void set_main_font ( const std::shared_ptr<font>& f ); protected: void on_mouse_move ( unsigned long state, long x, long y ); void on_drag ( ); void draw ( const canvas& c ) const; void on_mouse_down ( unsigned long btn, unsigned long state, long x, long y, bool is_double_click ); private: const long offset; named_rectangle nr; label x_label; label y_label; std::ostringstream sout; long click_x, click_y; // restricted functions mouse_tracker(mouse_tracker&); // copy constructor mouse_tracker& operator=(mouse_tracker&); // assignment operator }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // function message_box() // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- namespace message_box_helper { class box_win : public drawable_window { void initialize ( ); public: box_win ( const std::string& title_, const std::string& message_ ); box_win ( const std::wstring& title_, const std::wstring& message_ ); box_win ( const dlib::ustring& title_, const dlib::ustring& message_ ); ~box_win ( ); void set_click_handler ( const any_function<void()>& event_handler_ ) { auto_mutex M(wm); event_handler = event_handler_; } private: static void deleter_thread ( void* param ); void on_click ( ); on_close_return_code on_window_close ( ); const std::wstring title; const std::wstring message; label msg; button btn_ok; any_function<void()> event_handler; }; class blocking_box_win : public drawable_window { void initialize ( ); public: blocking_box_win ( const std::string& title_, const std::string& message_ ); blocking_box_win ( const std::wstring& title_, const std::wstring& message_ ); blocking_box_win ( const dlib::ustring& title_, const dlib::ustring& message_ ); ~blocking_box_win ( ); private: void on_click ( ); const std::wstring title; const std::wstring message; label msg; button btn_ok; }; } template < typename T > void message_box ( const std::string& title, const std::string& message, T& object, void (T::*event_handler)() ) { using namespace message_box_helper; box_win* win = new box_win(title,message); win->set_click_handler(make_mfp(object,event_handler)); } inline void message_box ( const std::string& title, const std::string& message, const any_function<void()>& event_handler ) { using namespace message_box_helper; box_win* win = new box_win(title,message); win->set_click_handler(event_handler); } inline void message_box ( const std::string& title, const std::string& message ) { using namespace message_box_helper; new box_win(title,message); } inline void message_box_blocking ( const std::string& title, const std::string& message ) { using namespace message_box_helper; blocking_box_win w(title,message); w.wait_until_closed(); } // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class list_box // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- namespace list_box_helper{ template <typename S = std::string> class list_box : public scrollable_region, public enumerable<const S> { /*! INITIAL VALUE - ms_enabled == false - items.size() == 0 - last_selected = 0 CONVENTION - size() == items.size() - (*this)[i] == items[i].name - is_selected(i) == items[i].is_selected - ms_enabled == multiple_select_enabled() - items[i].width == the width of items[i].name as given by font::compute_size() - items[i].height == the height of items[i].name as given by font::compute_size() - last_selected == the last item the user selected !*/ public: list_box( drawable_window& w ); ~list_box( ); bool is_selected ( unsigned long index ) const; void select ( unsigned long index ); void unselect ( unsigned long index ); template < typename style_type > void set_style ( const style_type& style_ ) { auto_mutex M(m); style.reset(new style_type(style_)); scrollable_region::set_style(style_.get_scrollable_region_style()); parent.invalidate_rectangle(rect); } template <typename T> void get_selected ( T& list ) const { auto_mutex M(m); list.clear(); for (unsigned long i = 0; i < items.size(); ++i) { if (items[i].is_selected) { unsigned long idx = i; list.enqueue(idx); } } } template <typename T> void load ( const T& list ) { auto_mutex M(m); items.clear(); unsigned long i = 0; items.set_max_size(list.size()); items.set_size(list.size()); list.reset(); unsigned long max_width = 0; unsigned long total_height = 0; while (list.move_next()) { items[i].is_selected = false; items[i].name = list.element(); mfont->compute_size(items[i].name,items[i].width, items[i].height); if (items[i].width > max_width) max_width = items[i].width; total_height += items[i].height; ++i; } set_total_rect_size(max_width, total_height); parent.invalidate_rectangle(rect); last_selected = 0; } const S& operator[] ( unsigned long index ) const; bool multiple_select_enabled ( ) const; void enable_multiple_select ( ); void disable_multiple_select ( ); template < typename T > void set_double_click_handler ( T& object, void (T::*eh)(unsigned long index) ) { auto_mutex M(m); event_handler = make_mfp(object,eh); } void set_double_click_handler ( const any_function<void(unsigned long)>& eh ) { auto_mutex M(m); event_handler = eh; } template < typename T > void set_click_handler ( T& object, void (T::*eh)(unsigned long index) ) { auto_mutex M(m); single_click_event_handler = make_mfp(object,eh); } void set_click_handler ( const any_function<void(unsigned long)>& eh ) { auto_mutex M(m); single_click_event_handler = eh; } bool at_start ( ) const; void reset ( ) const; bool current_element_valid ( ) const; const S& element ( ) const; const S& element ( ); bool move_next ( ) const; unsigned long size ( ) const; unsigned long get_selected ( ) const; void set_main_font ( const std::shared_ptr<font>& f ); private: void on_mouse_down ( unsigned long btn, unsigned long state, long x, long y, bool is_double_click ); void draw ( const canvas& c ) const; template <typename SS> struct data { SS name; bool is_selected; unsigned long width; unsigned long height; }; bool ms_enabled; array<data<S> > items; any_function<void(unsigned long)> event_handler; any_function<void(unsigned long)> single_click_event_handler; unsigned long last_selected; std::unique_ptr<list_box_style> style; // restricted functions list_box(list_box&); // copy constructor list_box& operator=(list_box&); // assignment operator }; } typedef list_box_helper::list_box<std::string> list_box; typedef list_box_helper::list_box<std::wstring> wlist_box; typedef list_box_helper::list_box<dlib::ustring> ulist_box; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // function open_file_box() // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- namespace open_file_box_helper { class box_win : public drawable_window { public: box_win ( const std::string& title, bool has_text_field = false ); ~box_win ( ); void set_click_handler ( const any_function<void(const std::string&)>& event_handler_ ) { auto_mutex M(wm); event_handler = event_handler_; } private: void set_sizes( ); void on_window_resized ( ); void deleter_thread ( ); void enter_folder ( const std::string& folder_name ); void on_dirs_click ( unsigned long idx ); void on_files_click ( unsigned long idx ); void on_files_double_click ( unsigned long ); void on_cancel_click ( ); void on_open_click ( ); void on_path_button_click ( toggle_button& btn ); bool set_dir ( const std::string& dir ); void on_root_click ( ); on_close_return_code on_window_close ( ); label lbl_dirs; label lbl_files; label lbl_file_name; list_box lb_dirs; list_box lb_files; button btn_ok; button btn_cancel; toggle_button btn_root; text_field tf_file_name; std::string path; std::string prefix; int cur_dir; any_function<void(const std::string&)> event_handler; sequence<std::unique_ptr<toggle_button> >::kernel_2a_c sob; }; } template < typename T > void open_file_box ( T& object, void (T::*event_handler)(const std::string&) ) { using namespace open_file_box_helper; box_win* win = new box_win("Open File",true); win->set_click_handler(make_mfp(object,event_handler)); } inline void open_file_box ( const any_function<void(const std::string&)>& event_handler ) { using namespace open_file_box_helper; box_win* win = new box_win("Open File",true); win->set_click_handler(event_handler); } template < typename T > void open_existing_file_box ( T& object, void (T::*event_handler)(const std::string&) ) { using namespace open_file_box_helper; box_win* win = new box_win("Open File"); win->set_click_handler(make_mfp(object,event_handler)); } inline void open_existing_file_box ( const any_function<void(const std::string&)>& event_handler ) { using namespace open_file_box_helper; box_win* win = new box_win("Open File"); win->set_click_handler(event_handler); } template < typename T > void save_file_box ( T& object, void (T::*event_handler)(const std::string&) ) { using namespace open_file_box_helper; box_win* win = new box_win("Save File",true); win->set_click_handler(make_mfp(object,event_handler)); } inline void save_file_box ( const any_function<void(const std::string&)>& event_handler ) { using namespace open_file_box_helper; box_win* win = new box_win("Save File",true); win->set_click_handler(event_handler); } // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class menu_bar // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class menu_bar : public drawable { /*! INITIAL VALUE - menus.size() == 0 - open_menu == 0 CONVENTION - size() == menus.size() - all menu data is stored in menus - menus[x].name == the name of the xth menu - if (menus[x].underline_pos != std::string::npos) then - menus[x].underline_pos == the position of the character in the menu name that should be underlined - menus[x].underline_p1 != menus[x].underline_p2 and these two points define the underline bar - else - menus[x].underline_p1 == menus[x].underline_p2 - menus[x].menu == menu(x) - menus[x].rect == the rectangle in which menus[x].name is drawn - menus[x].bgrect == the rectangle for the xth menu button - if (there is an open menu on the screen) then - open_menu == the index of the open menu from menus - else - open_menu == menus.size() !*/ public: menu_bar( drawable_window& w ); ~menu_bar(); // this function does nothing void set_pos(long,long){} void set_main_font ( const std::shared_ptr<font>& f ); void set_number_of_menus ( unsigned long num ); unsigned long number_of_menus ( ) const; void set_menu_name ( unsigned long idx, const std::string name, char underline_ch = '\0' ); void set_menu_name ( unsigned long idx, const std::wstring name, char underline_ch = '\0' ); void set_menu_name ( unsigned long idx, const dlib::ustring name, char underline_ch = '\0' ); const std::string menu_name ( unsigned long idx ) const; const std::wstring menu_wname ( unsigned long idx ) const; const dlib::ustring menu_uname ( unsigned long idx ) const; popup_menu& menu ( unsigned long idx ); const popup_menu& menu ( unsigned long idx ) const; protected: void on_window_resized ( ); void draw ( const canvas& c ) const; void on_window_moved ( ); void on_focus_lost ( ); void on_mouse_down ( unsigned long btn, unsigned long , long x, long y, bool ); void on_mouse_move ( unsigned long , long x, long y ); void on_keydown ( unsigned long key, bool is_printable, unsigned long state ); private: void show_menu ( unsigned long i ); void hide_menu ( ); void on_popup_hide ( ); void compute_menu_geometry ( ); void adjust_position ( ); struct menu_data { menu_data():underline_pos(dlib::ustring::npos){} dlib::ustring name; dlib::ustring::size_type underline_pos; popup_menu menu; rectangle rect; rectangle bgrect; point underline_p1; point underline_p2; }; array<menu_data> menus; unsigned long open_menu; // restricted functions menu_bar(menu_bar&); // copy constructor menu_bar& operator=(menu_bar&); // assignment operator }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class directed_graph_drawer // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- template <typename graph_type> class directed_graph_drawer : public zoomable_region { /*! INITIAL VALUE - edge_selected == false - mouse_drag == false - selected_node == 0 - graph_.number_of_nodes() == 0 - external_graph.number_of_nodes() == 0 - radius == 25 - last_mouse_click_in_display == false CONVENTION - radius == the radius of the nodes when they aren't zoomed - external_graph and graph_ have the same graph structure - external_graph == graph() - external_graph.node(i) == graph_node(i) - if (one of the nodes is selected) then - selected_node < graph_.number_of_nodes() - graph_.node(selected_node) == the selected node - else - selected_node == graph_.number_of_nodes() - if (the user is dragging a node with the mouse) then - mouse_drag == true - drag_offset == the vector from the mouse position to the center of the node - else - mouse_drag == false - if (the user has selected an edge) then - edge_selected == true - the parent node is graph_.node(selected_edge_parent) - the child node is graph_.node(selected_edge_parent) - else - edge_selected == false - for all valid i: - graph_.node(i).data.p == the center of the node in graph space - graph_.node(i).data.name == node_label(i) - graph_.node(i).data.color == node_color(i) - graph_.node(i).data.str_rect == a rectangle sized to contain graph_.node(i).data.name - if (the last mouse click in our parent window as in our display_rect_ ) then - last_mouse_click_in_display == true - else - last_mouse_click_in_display == false !*/ public: directed_graph_drawer ( drawable_window& w ) : zoomable_region(w,MOUSE_CLICK | MOUSE_WHEEL | KEYBOARD_EVENTS), radius(25), edge_selected(false), last_mouse_click_in_display(false) { mouse_drag = false; selected_node = 0; // Whenever you make your own drawable (or inherit from draggable or button_action) // you have to remember to call this function to enable the events. The idea // here is that you can perform whatever setup you need to do to get your // object into a valid state without needing to worry about event handlers // triggering before you are ready. enable_events(); } ~directed_graph_drawer ( ) { // Disable all further events for this drawable object. We have to do this // because we don't want draw() events coming to this object while or after // it has been destructed. disable_events(); // Tell the parent window to redraw its area that previously contained this // drawable object. parent.invalidate_rectangle(rect); } void clear_graph ( ) { auto_mutex M(m); graph_.clear(); external_graph.clear(); parent.invalidate_rectangle(display_rect()); } const typename graph_type::node_type& graph_node ( unsigned long i ) const { DLIB_ASSERT ( i < number_of_nodes() , "\tgraph_type::node_type& directed_graph_drawer::graph_node(i)" << "\n\ti: " << i << "\n\tnumber_of_nodes(): " << number_of_nodes() ); return external_graph.node(i); } typename graph_type::node_type& graph_node ( unsigned long i ) { DLIB_ASSERT ( i < number_of_nodes() , "\tgraph_type::node_type& directed_graph_drawer::graph_node(i)" << "\n\ti: " << i << "\n\tnumber_of_nodes(): " << number_of_nodes() ); return external_graph.node(i); } const graph_type& graph ( ) const { return external_graph; } void save_graph ( std::ostream& out ) { auto_mutex M(m); serialize(external_graph, out); serialize(graph_, out); parent.invalidate_rectangle(display_rect()); } void load_graph ( std::istream& in ) { auto_mutex M(m); deserialize(external_graph, in); deserialize(graph_, in); parent.invalidate_rectangle(display_rect()); } unsigned long number_of_nodes ( ) const { auto_mutex M(m); return graph_.number_of_nodes(); } void set_node_label ( unsigned long i, const std::string& label ) { set_node_label(i, convert_mbstring_to_wstring(label)); } void set_node_label ( unsigned long i, const std::wstring& label ) { set_node_label(i, convert_wstring_to_utf32(label)); } void set_node_label ( unsigned long i, const dlib::ustring& label ) { auto_mutex M(m); DLIB_ASSERT ( i < number_of_nodes() , "\tvoid directed_graph_drawer::set_node_label(i,label)" << "\n\ti: " << i << "\n\tlabel: " << narrow(label) << "\n\tnumber_of_nodes(): " << number_of_nodes() ); graph_.node(i).data.name = label.c_str(); unsigned long width, height; mfont->compute_size(label,width,height); graph_.node(i).data.str_rect = rectangle(width,height); parent.invalidate_rectangle(display_rect()); } void set_node_color ( unsigned long i, rgb_pixel color ) { auto_mutex M(m); DLIB_ASSERT ( i < number_of_nodes() , "\tvoid directed_graph_drawer::set_node_color(i,label)" << "\n\ti: " << i << "\n\tnumber_of_nodes(): " << number_of_nodes() ); graph_.node(i).data.color = color; parent.invalidate_rectangle(display_rect()); } rgb_pixel node_color ( unsigned long i ) const { auto_mutex M(m); DLIB_ASSERT ( i < number_of_nodes() , "\trgb_pixel directed_graph_drawer::node_color(i)" << "\n\ti: " << i << "\n\tnumber_of_nodes(): " << number_of_nodes() ); return graph_.node(i).data.color; } const std::string node_label ( unsigned long i ) const { auto_mutex M(m); DLIB_ASSERT ( i < number_of_nodes() , "\tconst std::ustring directed_graph_drawer::node_label(i)" << "\n\ti: " << i << "\n\tnumber_of_nodes(): " << number_of_nodes() ); return narrow(graph_.node(i).data.name); } const std::wstring node_wlabel ( unsigned long i ) const { return convert_utf32_to_wstring(node_ulabel(i)); } const dlib::ustring node_ulabel ( unsigned long i ) const { auto_mutex M(m); DLIB_ASSERT ( i < number_of_nodes() , "\tconst std::ustring directed_graph_drawer::node_label(i)" << "\n\ti: " << i << "\n\tnumber_of_nodes(): " << number_of_nodes() ); return graph_.node(i).data.name.c_str(); } template < typename T > void set_node_selected_handler ( T& object, void (T::*event_handler_)(unsigned long) ) { auto_mutex M(m); node_selected_handler = make_mfp(object,event_handler_); } void set_node_selected_handler ( const any_function<void(unsigned long)>& event_handler_ ) { auto_mutex M(m); node_selected_handler = event_handler_; } template < typename T > void set_node_deselected_handler ( T& object, void (T::*event_handler_)(unsigned long) ) { auto_mutex M(m); node_deselected_handler = make_mfp(object,event_handler_); } void set_node_deselected_handler ( const any_function<void(unsigned long)>& event_handler_ ) { auto_mutex M(m); node_deselected_handler = event_handler_; } template < typename T > void set_node_deleted_handler ( T& object, void (T::*event_handler_)() ) { auto_mutex M(m); node_deleted_handler = make_mfp(object,event_handler_); } void set_node_deleted_handler ( const any_function<void()>& event_handler_ ) { auto_mutex M(m); node_deleted_handler = event_handler_; } template < typename T > void set_graph_modified_handler ( T& object, void (T::*event_handler_)() ) { auto_mutex M(m); graph_modified_handler = make_mfp(object,event_handler_); } void set_graph_modified_handler ( const any_function<void()>& event_handler_ ) { auto_mutex M(m); graph_modified_handler = event_handler_; } protected: void on_keydown ( unsigned long key, bool , unsigned long ) { // ignore all keyboard input if the last thing the user clicked on // wasn't the display area if (last_mouse_click_in_display == false) return; // if a node is selected if (selected_node != graph_.number_of_nodes()) { // deselect the node if the user hits escape if (key == base_window::KEY_ESC) { parent.invalidate_rectangle(display_rect()); if (node_deselected_handler.is_set()) node_deselected_handler(selected_node); selected_node = graph_.number_of_nodes(); } // delete the node if the user hits delete if (key == base_window::KEY_DELETE || key == base_window::KEY_BACKSPACE) { parent.invalidate_rectangle(display_rect()); graph_.remove_node(selected_node); external_graph.remove_node(selected_node); selected_node = graph_.number_of_nodes(); mouse_drag = false; if (graph_modified_handler.is_set()) graph_modified_handler(); if (node_deleted_handler.is_set()) node_deleted_handler(); } } // if an edge is selected if (edge_selected) { // deselect the node if the user hits escape if (key == base_window::KEY_ESC) { parent.invalidate_rectangle(display_rect()); edge_selected = false; } // delete the node if the user hits delete if (key == base_window::KEY_DELETE || key == base_window::KEY_BACKSPACE) { parent.invalidate_rectangle(display_rect()); graph_.remove_edge(selected_edge_parent, selected_edge_child); external_graph.remove_edge(selected_edge_parent, selected_edge_child); edge_selected = false; if (graph_modified_handler.is_set()) graph_modified_handler(); } } } void on_mouse_move ( unsigned long state, long x, long y ) { if (mouse_drag) { const point p(nearest_point(display_rect(),point(x,y))); point center = drag_offset + p; graph_.node(selected_node).data.p = gui_to_graph_space(center); parent.invalidate_rectangle(display_rect()); } else { zoomable_region::on_mouse_move(state,x,y); } // check if the mouse isn't being dragged anymore if ((state & base_window::LEFT) == 0) { mouse_drag = false; } } void on_mouse_up ( unsigned long btn, unsigned long state, long x, long y ) { mouse_drag = false; zoomable_region::on_mouse_up(btn,state,x,y); } void on_mouse_down ( unsigned long btn, unsigned long state, long x, long y, bool is_double_click ) { bool redraw = false; if (display_rect().contains(x,y) && (btn == base_window::RIGHT || btn == base_window::LEFT) && (state & base_window::SHIFT) == 0 ) { // start out saying no edge is selected if (edge_selected) { edge_selected = false; redraw = true; } bool click_hit_node = false; dlib::vector<double,2> p(gui_to_graph_space(point(x,y))); // check if this click is on an existing node for (unsigned long i = 0; i < graph_.number_of_nodes(); ++i) { dlib::vector<double,2> n(graph_.node(i).data.p); if ((p-n).length() < radius) { click_hit_node = true; point center = graph_to_gui_space(graph_.node(i).data.p); mouse_drag = true; drag_offset = center - point(x,y); // only do something if the click isn't on the currently // selected node if (selected_node != i) { // send out the deselected event if appropriate if (selected_node != graph_.number_of_nodes() && node_deselected_handler.is_set()) node_deselected_handler(selected_node); selected_node = i; redraw = true; if (node_selected_handler.is_set()) node_selected_handler(selected_node); } break; } } // if the click didn't hit any node then make sure nothing is selected if (click_hit_node == false && selected_node != graph_.number_of_nodes()) { if (node_deselected_handler.is_set()) node_deselected_handler(selected_node); selected_node = graph_.number_of_nodes(); redraw = true; } // check if this click is on an edge if we didn't click on a node if (click_hit_node == false) { for (unsigned long n = 0; n < graph_.number_of_nodes() && edge_selected == false; ++n) { const dlib::vector<double,2> parent_center(graph_to_gui_space(graph_.node(n).data.p)); for (unsigned long e = 0; e < graph_.node(n).number_of_children() && edge_selected == false; ++e) { const dlib::vector<double,2> child_center(graph_to_gui_space(graph_.node(n).child(e).data.p)); rectangle area; area += parent_center; area += child_center; // if the point(x,y) is between the two nodes then lets consider it further if (area.contains(point(x,y))) { p = point(x,y); const dlib::vector<double> z(0,0,1); // find the distance from the line between the two nodes const dlib::vector<double,2> perpendicular(z.cross(parent_center-child_center).normalize()); double distance = std::abs((child_center-p).dot(perpendicular)); if (distance < 8) { edge_selected = true; selected_edge_parent = n; selected_edge_child = graph_.node(n).child(e).index(); redraw = true; } } } } } // if the click didn't land on any node then add a new one if this was // a right mouse button click if (click_hit_node == false && btn == base_window::RIGHT) { const unsigned long n = graph_.add_node(); external_graph.add_node(); graph_.node(n).data.p = gui_to_graph_space(point(x,y)); redraw = true; selected_node = n; mouse_drag = false; if (graph_modified_handler.is_set()) graph_modified_handler(); if (node_selected_handler.is_set()) node_selected_handler(selected_node); } else if (selected_node == graph_.number_of_nodes()) { // in this case the click landed in the white area between nodes zoomable_region::on_mouse_down( btn, state, x, y, is_double_click); } } // If the user is shift clicking with the mouse then see if we // should add a new edge. if (display_rect().contains(x,y) && btn == base_window::LEFT && (state & base_window::SHIFT) && selected_node != graph_.number_of_nodes() ) { dlib::vector<double,2> p(gui_to_graph_space(point(x,y))); // check if this click is on an existing node for (unsigned long i = 0; i < graph_.number_of_nodes(); ++i) { dlib::vector<double,2> n(graph_.node(i).data.p); if ((p-n).length() < radius) { // add the edge if it doesn't already exist and isn't an edge back to // the same node if (graph_.has_edge(selected_node,i) == false && selected_node != i && graph_.has_edge(i, selected_node) == false) { graph_.add_edge(selected_node,i); external_graph.add_edge(selected_node,i); redraw = true; if (graph_modified_handler.is_set()) graph_modified_handler(); } break; } } } if (redraw) parent.invalidate_rectangle(display_rect()); if (display_rect().contains(x,y) == false) last_mouse_click_in_display = false; else last_mouse_click_in_display = true; } void draw ( const canvas& c ) const { zoomable_region::draw(c); rectangle area = c.intersect(display_rect()); if (area.is_empty() == true) return; if (enabled) fill_rect(c,display_rect(),255); else fill_rect(c,display_rect(),128); const unsigned long rad = static_cast<unsigned long>(radius*zoom_scale()); point center; // first draw all the edges for (unsigned long i = 0; i < graph_.number_of_nodes(); ++i) { center = graph_to_gui_space(graph_.node(i).data.p); const rectangle circle_area(centered_rect(center,2*(rad+8),2*(rad+8))); // draw lines to all this node's parents const dlib::vector<double> z(0,0,1); for (unsigned long j = 0; j < graph_.node(i).number_of_parents(); ++j) { point p(graph_to_gui_space(graph_.node(i).parent(j).data.p)); rgb_pixel color(0,0,0); // if this is the selected edge then draw it with red instead of black if (edge_selected && selected_edge_child == i && selected_edge_parent == graph_.node(i).parent(j).index()) { color.red = 255; // we need to be careful when drawing this line to not draw it over the node dots since it // has a different color from them and would look weird dlib::vector<double,2> v(p-center); v = v.normalize()*rad; draw_line(c,center+v,p-v ,color, area); } else { draw_line(c,center,p ,color, area); } // draw the triangle pointing to this node if (area.intersect(circle_area).is_empty() == false) { dlib::vector<double,2> v(p-center); v = v.normalize(); dlib::vector<double,2> cross = z.cross(v).normalize(); dlib::vector<double,2> r(center + v*rad); for (double i = 0; i < 8*zoom_scale(); i += 0.1) draw_line(c,(r+v*i)+cross*i, (r+v*i)-cross*i,color,area); } } } // now draw all the node dots for (unsigned long i = 0; i < graph_.number_of_nodes(); ++i) { center = graph_to_gui_space(graph_.node(i).data.p); const rectangle circle_area(centered_rect(center,2*(rad+8),2*(rad+8))); // draw the actual dot for this node if (area.intersect(circle_area).is_empty()==false) { rgb_alpha_pixel color; assign_pixel(color, graph_.node(i).data.color); // this node is in area so lets draw it and all of it's edges as well draw_solid_circle(c,center,rad-3,color,area); color.alpha = 240; draw_circle(c,center,rad-3,color,area); color.alpha = 200; draw_circle(c,center,rad-2.5,color,area); color.alpha = 160; draw_circle(c,center,rad-2.0,color,area); color.alpha = 120; draw_circle(c,center,rad-1.5,color,area); color.alpha = 80; draw_circle(c,center,rad-1.0,color,area); color.alpha = 40; draw_circle(c,center,rad-0.5,color,area); } if (i == selected_node) draw_circle(c,center,rad+5,rgb_pixel(0,0,255),area); } // now draw all the strings last for (unsigned long i = 0; i < graph_.number_of_nodes(); ++i) { center = graph_to_gui_space(graph_.node(i).data.p); rectangle circle_area(centered_rect(center,2*rad+3,2*rad+3)); if (area.intersect(circle_area).is_empty()==false) { rgb_pixel color = graph_.node(i).data.color; // invert this color color.red = 255-color.red; color.green = 255-color.green; color.blue = 255-color.blue; sout << i; unsigned long width, height; mfont->compute_size(sout.str(),width,height); rectangle str_rect(centered_rect(center, width,height)); if (circle_area.contains(str_rect)) { mfont->draw_string(c,str_rect,sout.str(),color,0,std::string::npos,area); // draw the label for this node if it isn't empty if(graph_.node(i).data.name.size() > 0) { rectangle str_rect(graph_.node(i).data.str_rect); str_rect = centered_rect(center.x(), center.y()-rad-mfont->height(), str_rect.width(), str_rect.height()); mfont->draw_string(c,str_rect,graph_.node(i).data.name,0,0,std::string::npos,area); } } sout.str(""); } } } private: struct data { data() : color(0,0,0) {} vector<double> p; dlib::ustring name; rectangle str_rect; rgb_pixel color; }; friend void serialize(const data& item, std::ostream& out) { serialize(item.p, out); serialize(item.name, out); serialize(item.str_rect, out); serialize(item.color, out); } friend void deserialize(data& item, std::istream& in) { deserialize(item.p, in); deserialize(item.name, in); deserialize(item.str_rect, in); deserialize(item.color, in); } mutable std::ostringstream sout; const double radius; unsigned long selected_node; bool mouse_drag; // true if the user is dragging a node point drag_offset; bool edge_selected; unsigned long selected_edge_parent; unsigned long selected_edge_child; any_function<void(unsigned long)> node_selected_handler; any_function<void(unsigned long)> node_deselected_handler; any_function<void()> node_deleted_handler; any_function<void()> graph_modified_handler; graph_type external_graph; // rebind the graph_ type to make us a graph_ of data structs typename graph_type::template rebind<data,char, typename graph_type::mem_manager_type>::other graph_; bool last_mouse_click_in_display; }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class text_grid // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class text_grid : public scrollable_region { /*! INITIAL VALUE - has_focus == false - vertical_scroll_increment() == 10 - horizontal_scroll_increment() == 10 - border_color_ == rgb_pixel(128,128,128) CONVENTION - grid.nr() == row_height.size() - grid.nc() == col_width.size() - border_color() == border_color_ - text(r,c) == grid[r][c].text - text_color(r,c) == grid[r][c].text_color - background_color(r,c) == grid[r][c].bg_color - if (the user has clicked on this widget and caused one of the boxes to have input focus) then - has_focus == true - grid[active_row][active_col] == the active text box - cursor_pos == the position of the cursor in the above box - if (the cursor should be displayed) then - show_cursor == true - else - show_cursor == false - else - has_focus == false !*/ public: text_grid ( drawable_window& w ); ~text_grid ( ); void set_grid_size ( unsigned long rows, unsigned long cols ); unsigned long number_of_columns ( ) const; unsigned long number_of_rows ( ) const; int next_free_user_event_number ( ) const; rgb_pixel border_color ( ) const; void set_border_color ( rgb_pixel color ); const std::string text ( unsigned long row, unsigned long col ) const; const std::wstring wtext ( unsigned long row, unsigned long col ) const; const dlib::ustring utext ( unsigned long row, unsigned long col ) const; void set_text ( unsigned long row, unsigned long col, const std::string& str ); void set_text ( unsigned long row, unsigned long col, const std::wstring& str ); void set_text ( unsigned long row, unsigned long col, const dlib::ustring& str ); const rgb_pixel text_color ( unsigned long row, unsigned long col ) const; void set_text_color ( unsigned long row, unsigned long col, const rgb_pixel color ); const rgb_pixel background_color ( unsigned long row, unsigned long col ) const; void set_background_color ( unsigned long row, unsigned long col, const rgb_pixel color ); bool is_editable ( unsigned long row, unsigned long col ) const; void set_editable ( unsigned long row, unsigned long col, bool editable ); void set_column_width ( unsigned long col, unsigned long width ); void set_row_height ( unsigned long row, unsigned long height ); void disable ( ); void hide ( ); template < typename T > void set_text_modified_handler ( T& object, void (T::*eh)(unsigned long, unsigned long) ) { text_modified_handler = make_mfp(object,eh); } void set_text_modified_handler ( const any_function<void(unsigned long, unsigned long)>& eh ) { text_modified_handler = eh; } private: void on_user_event ( int num ); void timer_action ( ); /*! ensures - flips the state of show_cursor !*/ void compute_bg_rects ( ); void compute_total_rect ( ); void on_keydown ( unsigned long key, bool is_printable, unsigned long state ); void on_mouse_down ( unsigned long btn, unsigned long state, long x, long y, bool is_double_click ); void on_mouse_up ( unsigned long btn, unsigned long state, long x, long y ); void on_focus_lost ( ); void draw ( const canvas& c ) const; rectangle get_text_rect ( unsigned long row, unsigned long col ) const; rectangle get_bg_rect ( unsigned long row, unsigned long col ) const; struct data_type { data_type(): text_color(0,0,0), bg_color(255,255,255), first(0), is_editable(true) {} dlib::ustring text; rgb_pixel text_color; rgb_pixel bg_color; rectangle bg_rect; dlib::ustring::size_type first; bool is_editable; }; void drop_input_focus ( ); void move_cursor ( long row, long col, long new_cursor_pos ); array2d<data_type> grid; array<unsigned long> col_width; array<unsigned long> row_height; bool has_focus; long active_col; long active_row; long cursor_pos; bool show_cursor; bool recent_cursor_move; timer<text_grid> cursor_timer; rgb_pixel border_color_; any_function<void(unsigned long, unsigned long)> text_modified_handler; }; // ---------------------------------------------------------------------------------------- class image_display : public scrollable_region { /*! INITIAL VALUE - img.size() == 0 - overlay_rects.size() == 0 - overlay_lines.size() == 0 - drawing_rect == false - rect_is_selected == false CONVENTION - img == the image this object displays - overlay_rects == the overlay rectangles this object displays - overlay_lines == the overlay lines this object displays - if (drawing_rect) then - the user is drawing a rectangle on the screen and is thus holding down CTRL and the left mouse button. - rect_anchor == the point on the screen where the user clicked to begin drawing the rectangle. - rect_to_draw == the rectangle which should appear on the screen. - if (rect_is_selected) then - selected_rect == the index in overlay_rects of the user selected rectangle. - last_right_click_pos == the last place we saw the user right click the mouse. - parts_menu.is_enabled() == true - if (it is actually a part of this rect that is selected) then - selected_part_name == the name of the part in overlay_rects[selected_rect].parts that is selected. - else - selected_part_name.size() == 0 - else - parts_menu.is_enabled() == false - selected_part_name.size() == 0 - if (moving_overlay) then - moving_rect == the index in overlay_rects that the move applies to. - if (moving_what == MOVING_PART) then - moving_part_name == the name of the part in overlay_rects[moving_rect] that is being moved around with the mouse. - else - moving_what will tell us which side of the rectangle in overlay_rects[moving_rect] is being moved by the mouse. !*/ public: image_display( drawable_window& w ); ~image_display( ); template < typename image_type > void set_image ( const image_type& new_img ) { auto_mutex M(m); // if the new image has a different size when compared to the previous image // then we should readjust the total rectangle size. if (num_rows(new_img) != img.nr() || num_columns(new_img) != img.nc()) { if (zoom_in_scale != 1) set_total_rect_size(num_columns(new_img)*zoom_in_scale, num_rows(new_img)*zoom_in_scale); else set_total_rect_size(num_columns(new_img)/zoom_out_scale, num_rows(new_img)/zoom_out_scale); } else { parent.invalidate_rectangle(rect); } highlighted_rect = std::numeric_limits<unsigned long>::max(); rect_is_selected = false; parts_menu.disable(); assign_image_scaled(img,new_img); } virtual void set_pos ( long x, long y ) { auto_mutex lock(m); scrollable_region::set_pos(x,y); parts_menu.set_rect(rect); } virtual void set_size ( unsigned long width, unsigned long height ) { auto_mutex lock(m); scrollable_region::set_size(width,height); parts_menu.set_rect(rect); } struct overlay_rect { overlay_rect() :crossed_out(false) { assign_pixel(color, 0);} template <typename pixel_type> overlay_rect(const rectangle& r, pixel_type p) : rect(r),crossed_out(false) { assign_pixel(color, p); } template <typename pixel_type> overlay_rect(const rectangle& r, pixel_type p, const std::string& l) : rect(r),label(l),crossed_out(false) { assign_pixel(color, p); } template <typename pixel_type> overlay_rect(const rectangle& r, pixel_type p, const std::string& l, const std::map<std::string,point>& parts_) : rect(r),label(l),parts(parts_),crossed_out(false) { assign_pixel(color, p); } rectangle rect; rgb_alpha_pixel color; std::string label; std::map<std::string,point> parts; bool crossed_out; }; struct overlay_line { overlay_line() { assign_pixel(color, 0);} template <typename pixel_type> overlay_line(const point& p1_, const point& p2_, pixel_type p) : p1(p1_), p2(p2_) { assign_pixel(color, p); } point p1; point p2; rgb_alpha_pixel color; }; struct overlay_circle { overlay_circle():radius(0) { assign_pixel(color, 0);} template <typename pixel_type> overlay_circle(const point& center_, const int radius_, pixel_type p) : center(center_), radius(radius_) { assign_pixel(color, p); } template <typename pixel_type> overlay_circle(const point& center_, const int radius_, pixel_type p, const std::string& l) : center(center_), radius(radius_), label(l) { assign_pixel(color, p); } point center; int radius; rgb_alpha_pixel color; std::string label; }; void add_overlay ( const overlay_rect& overlay ); void add_overlay ( const overlay_line& overlay ); void add_overlay ( const overlay_circle& overlay ); void add_overlay ( const std::vector<overlay_rect>& overlay ); void add_overlay ( const std::vector<overlay_line>& overlay ); void add_overlay ( const std::vector<overlay_circle>& overlay ); void clear_overlay ( ); rectangle get_image_display_rect ( ) const; std::vector<overlay_rect> get_overlay_rects ( ) const; void set_default_overlay_rect_label ( const std::string& label ); std::string get_default_overlay_rect_label ( ) const; void set_default_overlay_rect_color ( const rgb_alpha_pixel& color ); rgb_alpha_pixel get_default_overlay_rect_color ( ) const; template < typename T > void set_overlay_rects_changed_handler ( T& object, void (T::*event_handler_)() ) { auto_mutex M(m); event_handler = make_mfp(object,event_handler_); } void set_overlay_rects_changed_handler ( const any_function<void()>& event_handler_ ) { auto_mutex M(m); event_handler = event_handler_; } template < typename T > void set_overlay_rect_selected_handler ( T& object, void (T::*event_handler_)(const overlay_rect& orect) ) { auto_mutex M(m); orect_selected_event_handler = make_mfp(object,event_handler_); } void set_overlay_rect_selected_handler ( const any_function<void(const overlay_rect& orect)>& event_handler_ ) { auto_mutex M(m); orect_selected_event_handler = event_handler_; } template < typename T > void set_image_clicked_handler ( T& object, void (T::*event_handler_)(const point& p, bool is_double_click, unsigned long btn) ) { auto_mutex M(m); image_clicked_handler = make_mfp(object,event_handler_); } void set_image_clicked_handler ( const any_function<void(const point& p, bool is_double_click, unsigned long btn)>& event_handler_ ) { auto_mutex M(m); image_clicked_handler = event_handler_; } void add_labelable_part_name ( const std::string& name ); void clear_labelable_part_names ( ); void enable_overlay_editing ( ) { auto_mutex M(m); overlay_editing_enabled = true; } void disable_overlay_editing ( ) { auto_mutex M(m); overlay_editing_enabled = false; rect_is_selected = false; drawing_rect = false; parent.invalidate_rectangle(rect); } bool overlay_editing_is_enabled ( ) const { auto_mutex M(m); return overlay_editing_enabled; } private: void draw ( const canvas& c ) const; void on_wheel_up ( unsigned long state ); void on_wheel_down ( unsigned long state ); void on_mouse_down ( unsigned long btn, unsigned long state, long x, long y, bool is_double_click ); void on_mouse_up ( unsigned long btn, unsigned long state, long x, long y ); void on_mouse_move ( unsigned long state, long x, long y ); void on_keydown ( unsigned long key, bool is_printable, unsigned long state ); void on_part_add ( const std::string& part_name ); rectangle get_rect_on_screen ( unsigned long idx ) const; rectangle get_rect_on_screen ( rectangle orect ) const; rgb_alpha_pixel invert_pixel (const rgb_alpha_pixel& p) const { return rgb_alpha_pixel(255-p.red, 255-p.green, 255-p.blue, p.alpha); } virtual int next_free_user_event_number ( ) const { return scrollable_region::next_free_user_event_number()+1; } // The reason for using user actions here rather than just having the timer just call // what it needs directly is to avoid a potential deadlock during destruction of this widget. void timer_event_unhighlight_rect() { highlight_timer.stop(); parent.trigger_user_event(this,scrollable_region::next_free_user_event_number()); } void on_user_event (int num) { // ignore this user event if it isn't for us if (num != scrollable_region::next_free_user_event_number()) return; if (highlighted_rect < overlay_rects.size()) { highlighted_rect = std::numeric_limits<unsigned long>::max(); parent.invalidate_rectangle(rect); } } array2d<rgb_alpha_pixel> img; std::vector<overlay_rect> overlay_rects; std::vector<overlay_line> overlay_lines; std::vector<overlay_circle> overlay_circles; long zoom_in_scale; long zoom_out_scale; bool drawing_rect; point rect_anchor; rectangle rect_to_draw; bool rect_is_selected; std::string selected_part_name; unsigned long selected_rect; rgb_alpha_pixel default_rect_color; std::string default_rect_label; any_function<void()> event_handler; any_function<void(const overlay_rect& orect)> orect_selected_event_handler; any_function<void(const point& p, bool is_double_click, unsigned long btn)> image_clicked_handler; popup_menu_region parts_menu; point last_right_click_pos; const double part_width; std::set<std::string> part_names; bool overlay_editing_enabled; timer<image_display> highlight_timer; unsigned long highlighted_rect; bool holding_shift_key; bool moving_overlay; unsigned long moving_rect; enum { MOVING_RECT_LEFT, MOVING_RECT_TOP, MOVING_RECT_RIGHT, MOVING_RECT_BOTTOM, MOVING_PART } moving_what; std::string moving_part_name; // restricted functions image_display(image_display&); // copy constructor image_display& operator=(image_display&); // assignment operator }; // ---------------------------------------------------------------------------------------- class perspective_display : public drawable, noncopyable { public: perspective_display( drawable_window& w ); ~perspective_display( ); virtual void set_size ( unsigned long width, unsigned long height ); struct overlay_line { overlay_line() { assign_pixel(color, 0);} overlay_line(const vector<double>& p1_, const vector<double>& p2_) : p1(p1_), p2(p2_) { assign_pixel(color, 255); } template <typename pixel_type> overlay_line(const vector<double>& p1_, const vector<double>& p2_, pixel_type p) : p1(p1_), p2(p2_) { assign_pixel(color, p); } vector<double> p1; vector<double> p2; rgb_pixel color; }; struct overlay_dot { overlay_dot() { assign_pixel(color, 0);} overlay_dot(const vector<double>& p_) : p(p_) { assign_pixel(color, 255); } template <typename pixel_type> overlay_dot(const vector<double>& p_, pixel_type color_) : p(p_) { assign_pixel(color, color_); } vector<double> p; rgb_pixel color; }; void add_overlay ( const std::vector<overlay_line>& overlay ); void add_overlay ( const std::vector<overlay_dot>& overlay ); void clear_overlay ( ); template < typename T > void set_dot_double_clicked_handler ( T& object, void (T::*event_handler_)(const vector<double>&) ) { auto_mutex M(m); dot_clicked_event_handler = make_mfp(object,event_handler_); } void set_dot_double_clicked_handler ( const any_function<void(const vector<double>&)>& event_handler_ ); private: void draw ( const canvas& c ) const; void on_wheel_up ( unsigned long state ); void on_wheel_down ( unsigned long state ); void on_mouse_down ( unsigned long btn, unsigned long state, long x, long y, bool is_double_click ); void on_mouse_move ( unsigned long state, long x, long y ); static bool compare_second ( const std::pair<overlay_dot,float>& a, const std::pair<overlay_dot,float>& b ) { return a.second < b.second; } point last; std::vector<overlay_line> overlay_lines; std::vector<overlay_dot> overlay_dots; camera_transform tform; vector<double> sum_pts; vector<double> max_pts; any_function<void(const vector<double>&)> dot_clicked_event_handler; mutable array2d<float> depth; }; // ---------------------------------------------------------------------------------------- class perspective_window : public drawable_window, noncopyable { public: typedef perspective_display::overlay_line overlay_line; typedef perspective_display::overlay_dot overlay_dot; perspective_window( ) : disp(*this) { set_size(100,100); on_window_resized(); show(); } perspective_window( const std::vector<dlib::vector<double> >& point_cloud ) : disp(*this) { set_size(100,100); on_window_resized(); add_overlay(point_cloud); show(); } perspective_window( const std::vector<dlib::vector<double> >& point_cloud, const std::string& title ) : disp(*this) { set_size(100,100); on_window_resized(); add_overlay(point_cloud); set_title(title); show(); } ~perspective_window( ) { // You should always call close_window() in the destructor of window // objects to ensure that no events will be sent to this window while // it is being destructed. close_window(); } void add_overlay ( const std::vector<overlay_line>& overlay ) { disp.add_overlay(overlay); } void add_overlay ( const std::vector<overlay_dot>& overlay ) { disp.add_overlay(overlay); } void clear_overlay ( ) { disp.clear_overlay(); } template <typename pixel_type> void add_overlay(const vector<double>& p1, const vector<double>& p2, pixel_type p) { add_overlay(std::vector<overlay_line>(1,overlay_line(p1,p2,p))); } void add_overlay(const std::vector<dlib::vector<double> >& d) { add_overlay(d, 255); } template <typename pixel_type> void add_overlay(const std::vector<dlib::vector<double> >& d, pixel_type p) { std::vector<overlay_dot> temp; temp.resize(d.size()); for (unsigned long i = 0; i < temp.size(); ++i) temp[i] = overlay_dot(d[i], p); add_overlay(temp); } template < typename T > void set_dot_double_clicked_handler ( T& object, void (T::*event_handler_)(const vector<double>&) ) { disp.set_dot_double_clicked_handler(object,event_handler_); } void set_dot_double_clicked_handler ( const any_function<void(const vector<double>&)>& event_handler_ ) { disp.set_dot_double_clicked_handler(event_handler_); } private: void on_window_resized( ) { drawable_window::on_window_resized(); unsigned long width, height; get_size(width,height); disp.set_pos(0,0); disp.set_size(width, height); } perspective_display disp; }; // ---------------------------------------------------------------------------------------- class image_window : public drawable_window { public: typedef image_display::overlay_rect overlay_rect; typedef image_display::overlay_line overlay_line; typedef image_display::overlay_circle overlay_circle; image_window( ); template < typename image_type > image_window( const image_type& img ) : gui_img(*this), window_has_closed(false), have_last_click(false), mouse_btn(0), clicked_signaler(this->wm), have_last_keypress(false), tie_input_events(false) { gui_img.set_image_clicked_handler(*this, &image_window::on_image_clicked); gui_img.disable_overlay_editing(); set_image(img); show(); } template < typename image_type > image_window( const image_type& img, const std::string& title ) : gui_img(*this), window_has_closed(false), have_last_click(false), mouse_btn(0), clicked_signaler(this->wm), have_last_keypress(false), tie_input_events(false) { gui_img.set_image_clicked_handler(*this, &image_window::on_image_clicked); gui_img.disable_overlay_editing(); set_image(img); set_title(title); show(); } ~image_window( ); template < typename image_type > void set_image ( const image_type& img ) { const unsigned long padding = scrollable_region_style_default().get_border_size(); auto_mutex M(wm); gui_img.set_image(img); // Only ever mess with the size of the window if the user is giving us an image // that is a different size. Otherwise we assume that they will have already // sized the window to whatever they feel is reasonable for an image of the // current size. if (previous_image_size != get_rect(img)) { const rectangle r = gui_img.get_image_display_rect(); if (image_rect != r) { // set the size of this window to match the size of the input image set_size(r.width()+padding*2,r.height()+padding*2); // call this to make sure everything else is setup properly on_window_resized(); image_rect = r; } previous_image_size = get_rect(img); } } void add_overlay ( const overlay_rect& overlay ); template <typename pixel_type> void add_overlay(const rectangle& r, pixel_type p) { add_overlay(image_display::overlay_rect(r,p)); } void add_overlay(const rectangle& r) { add_overlay(image_display::overlay_rect(r,rgb_pixel(255,0,0))); } template <typename pixel_type> void add_overlay(const rectangle& r, pixel_type p, const std::string& l) { add_overlay(image_display::overlay_rect(r,p,l)); } template <typename pixel_type> void add_overlay(const std::vector<rectangle>& r, pixel_type p) { std::vector<overlay_rect> temp; temp.resize(r.size()); for (unsigned long i = 0; i < temp.size(); ++i) temp[i] = overlay_rect(r[i], p); add_overlay(temp); } void add_overlay(const std::vector<rectangle>& r) { add_overlay(r, rgb_pixel(255,0,0)); } void add_overlay( const full_object_detection& object, const std::vector<std::string>& part_names ) { add_overlay(overlay_rect(object.get_rect(), rgb_pixel(255,0,0))); std::vector<overlay_circle> temp; temp.reserve(object.num_parts()); for (unsigned long i = 0; i < object.num_parts(); ++i) { if (object.part(i) != OBJECT_PART_NOT_PRESENT) { if (i < part_names.size()) temp.push_back(overlay_circle(object.part(i), 7, rgb_pixel(0,255,0), part_names[i])); else temp.push_back(overlay_circle(object.part(i), 7, rgb_pixel(0,255,0))); } } add_overlay(temp); } void add_overlay( const full_object_detection& object ) { std::vector<std::string> part_names; add_overlay(object, part_names); } void add_overlay( const std::vector<full_object_detection>& objects, const std::vector<std::string>& part_names ) { std::vector<overlay_rect> rtemp; rtemp.reserve(objects.size()); for (unsigned long i = 0; i < objects.size(); ++i) { rtemp.push_back(overlay_rect(objects[i].get_rect(), rgb_pixel(255,0,0))); } add_overlay(rtemp); std::vector<overlay_circle> temp; for (unsigned long i = 0; i < objects.size(); ++i) { for (unsigned long j = 0; j < objects[i].num_parts(); ++j) { if (objects[i].part(j) != OBJECT_PART_NOT_PRESENT) { if (j < part_names.size()) temp.push_back(overlay_circle(objects[i].part(j), 7, rgb_pixel(0,255,0),part_names[j])); else temp.push_back(overlay_circle(objects[i].part(j), 7, rgb_pixel(0,255,0))); } } } add_overlay(temp); } void add_overlay( const std::vector<full_object_detection>& objects ) { std::vector<std::string> part_names; add_overlay(objects, part_names); } void add_overlay ( const overlay_line& overlay ); void add_overlay ( const overlay_circle& overlay ); template <typename pixel_type> void add_overlay(const point& p1, const point& p2, pixel_type p) { add_overlay(image_display::overlay_line(p1,p2,p)); } void add_overlay ( const std::vector<overlay_rect>& overlay ); void add_overlay ( const std::vector<overlay_line>& overlay ); void add_overlay ( const std::vector<overlay_circle>& overlay ); void clear_overlay ( ); bool get_next_double_click ( point& p, unsigned long& mouse_button ); void tie_events ( ); void untie_events ( ); bool events_tied ( ) const; bool get_next_double_click ( point& p ) { unsigned long mouse_button; return get_next_double_click(p, mouse_button); } bool get_next_keypress ( unsigned long& key, bool& is_printable, unsigned long& state ); bool get_next_keypress ( unsigned long& key, bool& is_printable ) { unsigned long state; return get_next_keypress(key,is_printable,state); } private: virtual base_window::on_close_return_code on_window_close( ); void on_window_resized( ); void on_image_clicked ( const point& p, bool is_double_click, unsigned long btn ); virtual void on_keydown ( unsigned long key, bool is_printable, unsigned long state ); // restricted functions image_window(image_window&); image_window& operator= (image_window&); image_display gui_img; rectangle image_rect; rectangle previous_image_size; bool window_has_closed; bool have_last_click; point last_clicked_point; unsigned long mouse_btn; rsignaler clicked_signaler; bool have_last_keypress; unsigned long next_key; bool next_is_printable; unsigned long next_state; bool tie_input_events; }; // ---------------------------------------------------------------------------------------- } #ifdef NO_MAKEFILE #include "widgets.cpp" #endif #endif // DLIB_WIDGETs_