dlib C++ Library - base_widgets_abstract.h

    // Copyright (C) 2005  Davis E. King (davisking@users.sourceforge.net), Keita Mochizuki
    // License: Boost Software License   See LICENSE.txt for the full license.
    #undef DLIB_BASE_WIDGETs_ABSTRACT_
    #ifdef DLIB_BASE_WIDGETs_ABSTRACT_

    #include "fonts_abstract.h"
    #include "drawable_abstract.h"

    #include "../gui_core.h"
    #include <string>

    namespace dlib
    {

        /*!
            GENERAL REMARKS
                This file contains objects that are useful for creating complex drawable
                widgets.

            THREAD SAFETY
                All objects and functions defined in this file are thread safe.  You may
                call them from any thread without serializing access to them.

            EVENT HANDLERS
                If you derive from any of the drawable objects and redefine any of the on_*()
                event handlers then you should ensure that your version calls the same event
                handler in the base object so that the base class part of your object will also
                be able to process the event.

                Also note that all event handlers, including the user registered callback
                functions, are executed in the event handling thread.
        !*/

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // class draggable
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        class draggable : public drawable
        {
            /*!
                INITIAL VALUE
                    draggable_area() == an initial value for its type

                WHAT THIS OBJECT REPRESENTS
                    This object represents a drawable object that is draggable by the mouse.
                    You use it by inheriting from it and defining the draw() method and any
                    of the on_*() event handlers you need.

                    This object is draggable by the user when is_enabled() == true and
                    not draggable otherwise.
            !*/

        public:

            draggable(
                drawable_window& w,
                unsigned long events = 0
            );
            /*!
                ensures
                    - #*this is properly initialized
                    - #*this has been added to window w
                    - #parent_window() == w
                    - This object will not receive any events or draw() requests until
                      enable_events() is called
                    - the events flags are passed on to the drawable object's
                      constructor.
                throws
                    - std::bad_alloc
                    - dlib::thread_error
            !*/

            virtual ~draggable(
            ) = 0;
            /*!
                ensures
                    - all resources associated with *this have been released
            !*/

            rectangle draggable_area (
            ) const;
            /*!
                ensures
                    - returns the area that this draggable can be dragged around in.
            !*/

            void set_draggable_area (
                const rectangle& area
            );
            /*!
                ensures
                    - #draggable_area() == area
            !*/

        protected:

            bool is_being_dragged (
            ) const;
            /*!
                requires
                    - mutex drawable::m is locked
                ensures
                    - if (this widget is currently being dragged by the user) then
                        - returns true
                    - else
                        - returns false
            !*/

            // does nothing by default
            virtual void on_drag (
            ){}
            /*!
                requires
                    - enable_events() has been called
                    - is_enabled() == true
                    - is_hidden() == false
                    - mutex drawable::m is locked
                    - is called when the user drags this object
                    - get_rect() == the rectangle that defines the new position
                      of this object.
                    - is_being_dragged() == true
                ensures
                    - does not change the state of mutex drawable::m.
            !*/

            // does nothing by default
            virtual void on_drag_stop (
            ){}
            /*!
                requires
                    - enable_events() has been called
                    - mutex drawable::m is locked
                    - is called when the user stops dragging this object
                    - is_being_dragged() == false
                ensures
                    - does not change the state of mutex drawable::m.
            !*/

        private:

            // restricted functions
            draggable(draggable&);        // copy constructor
            draggable& operator=(draggable&);    // assignment operator
        };

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // class mouse_over_event
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        class mouse_over_event : public drawable
        {
            /*!
                INITIAL VALUE
                    is_mouse_over() == false

                WHAT THIS OBJECT REPRESENTS
                    This object represents a drawable object with the additoin of two events
                    that will alert you when the mouse enters or leaves your drawable object.

                    You use it by inheriting from it and defining the draw() method and any
                    of the on_*() event handlers you need.
            !*/

        public:

            mouse_over_event(
                drawable_window& w,
                unsigned long events = 0
            );
            /*!
                ensures
                    - #*this is properly initialized
                    - #*this has been added to window w
                    - #parent_window() == w
                    - #*this will not receive any events or draw() requests until
                      enable_events() is called
                    - the events flags are passed on to the drawable object's
                      constructor.
                throws
                    - std::bad_alloc
                    - dlib::thread_error
            !*/

            virtual ~mouse_over_event(
            ) = 0;
            /*!
                ensures
                    - all resources associated with *this have been released
            !*/

        protected:

            bool is_mouse_over (
            ) const;
            /*!
                requires
                    - mutex drawable::m is locked
                ensures
                    - if (the mouse is currently over this widget) then
                        - returns true
                    - else
                        - returns false
            !*/

            // does nothing by default
            virtual void on_mouse_over (
            ){}
            /*!
                requires
                    - enable_events() has been called
                    - mutex drawable::m is locked
                    - is_enabled() == true
                    - is_hidden() == false
                    - is called whenever this object transitions from the state where
                      is_mouse_over() == false to is_mouse_over() == true
                ensures
                    - does not change the state of mutex drawable::m.
            !*/

            // does nothing by default
            virtual void on_mouse_not_over (
            ){}
            /*!
                requires
                    - enable_events() has been called
                    - mutex drawable::m is locked
                    - is called whenever this object transitions from the state where
                      is_mouse_over() == true to is_mouse_over() == false
                ensures
                    - does not change the state of mutex drawable::m.
            !*/

        private:

            // restricted functions
            mouse_over_event(mouse_over_event&);        // copy constructor
            mouse_over_event& operator=(mouse_over_event&);    // assignment operator
        };

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // class button_action
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        class button_action : public mouse_over_event
        {
            /*!
                INITIAL VALUE
                    is_depressed() == false

                WHAT THIS OBJECT REPRESENTS
                    This object represents the clicking action of a push button.  It provides
                    simple callbacks that can be used to make various kinds of button
                    widgets.

                    You use it by inheriting from it and defining the draw() method and any
                    of the on_*() event handlers you need.
            !*/

        public:

            button_action(
                drawable_window& w,
                unsigned long events = 0
            );
            /*!
                ensures
                    - #*this is properly initialized
                    - #*this has been added to window w
                    - #parent_window() == w
                    - #*this will not receive any events or draw() requests until
                      enable_events() is called
                    - the events flags are passed on to the drawable object's
                      constructor.
                throws
                    - std::bad_alloc
                    - dlib::thread_error
            !*/

            virtual ~button_action(
            ) = 0;
            /*!
                ensures
                    - all resources associated with *this have been released
            !*/

        protected:

            bool is_depressed (
            ) const;
            /*!
                requires
                    - mutex drawable::m is locked
                ensures
                    - if (this button is currently in a depressed state) then
                        - the user has left clicked on this drawable and is still
                          holding the left mouse button down over it.
                        - returns true
                    - else
                        - returns false
            !*/

            // does nothing by default
            virtual void on_button_down (
            ){}
            /*!
                requires
                    - enable_events() has been called
                    - mutex drawable::m is locked
                    - is_enabled() == true
                    - is_hidden() == false
                    - the area in parent_window() defined by get_rect() has been invalidated.
                      (This means you don't have to call invalidate_rectangle())
                    - is called whenever this object transitions from the state where
                      is_depressed() == false to is_depressed() == true
                ensures
                    - does not change the state of mutex drawable::m.
            !*/

            // does nothing by default
            virtual void on_button_up (
                bool mouse_over
            ){}
            /*!
                requires
                    - enable_events() has been called
                    - mutex drawable::m is locked
                    - the area in parent_window() defined by get_rect() has been invalidated.
                      (This means you don't have to call invalidate_rectangle())
                    - is called whenever this object transitions from the state where
                      is_depressed() == true to is_depressed() == false
                    - if (the mouse was over this button when this event occurred) then
                        - mouse_over == true
                    - else
                        - mouse_over == false
                ensures
                    - does not change the state of mutex drawable::m.
            !*/

        private:

            // restricted functions
            button_action(button_action&);        // copy constructor
            button_action& operator=(button_action&);    // assignment operator
        };

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // class button
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        class button : public button_action
        {
            /*!
                INITIAL VALUE
                    name() == ""
                    tooltip_text() == "" (i.e. there is no tooltip by default)

                WHAT THIS OBJECT REPRESENTS
                    This object represents a simple button.

                    When this object is disabled it means it will not respond to user clicks.
            !*/

        public:

            button(
                drawable_window& w
            );
            /*!
                ensures
                    - #*this is properly initialized
                    - #*this has been added to window w
                    - #parent_window() == w
                throws
                    - std::bad_alloc
                    - dlib::thread_error
            !*/

            virtual ~button(
            );
            /*!
                ensures
                    - all resources associated with *this have been released
            !*/

            void set_size (
                unsigned long width_,
                unsigned long height_
            );
            /*!
                ensures
                    - if (width and height are big enough to contain the name of this button) then
                        - #width() == width_
                        - #height() == height_
                        - #top() == top()
                        - #left() == left()
                        - i.e. The location of the upper left corner of this button stays the
                          same but its width and height are modified
            !*/

            void set_name (const std::wstring& name);
            void set_name (const dlib::ustring& name);
            void set_name (
                const std::string& name
            );
            /*!
                ensures
                    - #name() == name
                    - this button has been resized such that it is big enough to contain
                      the new name.
                throws
                    - std::bad_alloc
            !*/

            const std::wstring wname () const;
            const dlib::string uname () const;
            const std::string  name (
            ) const;
            /*!
                ensures
                    - returns the name of this button
                throws
                    - std::bad_alloc
            !*/

            void set_tooltip_text (const std::wstring& text);
            void set_tooltip_text (const dlib::ustring& text);
            void set_tooltip_text (
                const std::string& text
            );
            /*!
                ensures
                    - #tooltip_text() == text
                    - enables the tooltip for this button
            !*/

            const dlib::ustring tooltip_utext () const;
            const std::wstring  tooltip_wtext () const;
            const std::string   tooltip_text (
            ) const;
            /*!
                ensures
                    - returns the text that is displayed in the tooltip for this button
            !*/

            bool is_depressed (
            ) const;
            /*!
                ensures
                    - if (this button is currently in a depressed state) then
                        - the user has left clicked on this widget and is still
                          holding the left mouse button down over it.
                        - returns true
                    - else
                        - returns false
            !*/

            template <
                typename style_type
                >
            void set_style (
                const style_type& style
            );
            /*!
                requires
                    - style_type == a type that inherits from button_style
                ensures
                    - this button object will draw itself using the given
                      button style
            !*/

            template <
                typename T
                >
            void set_click_handler (
                T& object,
                void (T::*event_handler)()
            );
            /*!
                requires
                    - event_handler is a valid pointer to a member function in T
                ensures
                    - the event_handler function is called on object when the button is
                      clicked by the user.
                    - any previous calls to this function are overridden by this new call.
                      (i.e. you can only have one event handler associated with this
                      event at a time)
                throws
                    - std::bad_alloc
            !*/

            template <
                typename T
                >
            void set_click_handler (
                T& object,
                void (T::*event_handler)(button& self)
            );
            /*!
                requires
                    - event_handler is a valid pointer to a member function in T
                ensures
                    - &self == this
                    - the event_handler function is called on object when the button is
                      clicked by the user.
                    - any previous calls to this function are overridden by this new call.
                      (i.e. you can only have one event handler associated with this
                      event at a time)
                throws
                    - std::bad_alloc
            !*/

            template <
                typename T
                >
            void set_button_down_handler (
                T& object,
                void (T::*event_handler)()
            );
            /*!
                requires
                    - event_handler is a valid pointer to a member function in T
                ensures
                    - the event_handler function is called on object when the user causes
                      the button to go into its depressed state.
                    - any previous calls to this function are overridden by this new call.
                      (i.e. you can only have one event handler associated with this
                      event at a time)
                throws
                    - std::bad_alloc
            !*/

            template <
                typename T
                >
            void set_button_up_handler (
                T& object,
                void (T::*event_handler)(bool mouse_over)
            );
            /*!
                requires
                    - event_handler is a valid pointer to a member function in T
                ensures
                    - the event_handler function is called on object when the user causes
                      the button to go into its non-depressed state.
                    - if (the mouse is over this button when this event occurs) then
                        - mouse_over == true
                    - else
                        - mouse_over == false
                    - any previous calls to this function are overridden by this new call.
                      (i.e. you can only have one event handler associated with this
                      event at a time)
                throws
                    - std::bad_alloc
            !*/

            template <
                typename T
                >
            void set_button_down_handler (
                T& object,
                void (T::*event_handler)(button& self)
            );
            /*!
                requires
                    - event_handler is a valid pointer to a member function in T
                ensures
                    - &self == this
                    - the event_handler function is called on object when the user causes
                      the button to go into its depressed state.
                    - any previous calls to this function are overridden by this new call.
                      (i.e. you can only have one event handler associated with this
                      event at a time)
                throws
                    - std::bad_alloc
            !*/

            template <
                typename T
                >
            void set_button_up_handler (
                T& object,
                void (T::*event_handler)(bool mouse_over, button& self)
            );
            /*!
                requires
                    - event_handler is a valid pointer to a member function in T
                ensures
                    - &self == this
                    - the event_handler function is called on object when the user causes
                      the button to go into its non-depressed state.
                    - if (the mouse is over this button when this event occurs) then
                        - mouse_over == true
                    - else
                        - mouse_over == false
                    - any previous calls to this function are overridden by this new call.
                      (i.e. you can only have one event handler associated with this
                      event at a time)
                throws
                    - std::bad_alloc
            !*/

        private:

            // restricted functions
            button(button&);        // copy constructor
            button& operator=(button&);    // assignment operator
        };

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // class scroll_bar
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        class scroll_bar : public drawable
        {
            /*!
                INITIAL VALUE
                    orientation() == a value given to the constructor.
                    max_slider_pos() == 0
                    slider_pos() == 0
                    jump_size() == 10

                WHAT THIS OBJECT REPRESENTS
                    This object represents a scroll bar.  The slider_pos() of the scroll bar
                    ranges from 0 to max_slider_pos().  The 0 position of the scroll_bar is
                    in the top or left side of the scroll_bar depending on its orientation.

                    When this object is disabled it means it will not respond to user clicks.
            !*/

        public:
            enum bar_orientation
            {
                HORIZONTAL,
                VERTICAL
            };

            scroll_bar(
                drawable_window& w,
                bar_orientation orientation
            );
            /*!
                ensures
                    - #*this is properly initialized
                    - #*this has been added to window w
                    - #orientation() == orientation
                    - #parent_window() == w
                throws
                    - std::bad_alloc
                    - dlib::thread_error
            !*/

            virtual ~scroll_bar(
            );
            /*!
                ensures
                    - all resources associated with *this have been released
            !*/

            bar_orientation orientation (
            ) const;
            /*!
                ensures
                    - returns the orientation of this scroll_bar
            !*/

            template <
                typename style_type
                >
            void set_style (
                const style_type& style
            );
            /*!
                requires
                    - style_type == a type that inherits from scroll_bar_style
                ensures
                    - this scroll_bar object will draw itself using the given
                      scroll bar style
            !*/

            void set_length (
                unsigned long length,
            );
            /*!
                ensures
                    - if (orientation() == HORIZONTAL) then
                        - #width() == max(length,1)
                    - else
                        - #height() == max(length,1)
            !*/

            long max_slider_pos (
            ) const;
            /*!
                ensures
                    - returns the maximum value that slider_pos() can take.
            !*/

            void set_max_slider_pos (
                long mpos
            );
            /*!
                ensures
                    - if (mpos < 0) then
                        - #max_slider_pos() == 0
                    - else
                        - #max_slider_pos() == mpos
                    - if (slider_pos() > #max_slider_pos()) then
                        - #slider_pos() == #max_slider_pos()
                    - else
                        - #slider_pos() == slider_pos()
            !*/

            void set_slider_pos (
                unsigned long pos
            );
            /*!
                ensures
                    - if (pos < 0) then
                        - #slider_pos() == 0
                    - else if (pos > max_slider_pos()) then
                        - #slider_pos() == max_slider_pos()
                    - else
                        - #slider_pos() == pos
            !*/

            long slider_pos (
            ) const;
            /*!
                ensures
                    - returns the current position of the slider box within the scroll bar.
            !*/

            long jump_size (
            ) const;
            /*!
                ensures
                    - returns the number of positions that the slider bar will jump when the
                      user clicks on the empty gaps above or below the slider bar.
                      (note that the slider will jump less than the jump size if it hits the
                      end of the scroll bar)
            !*/

            void set_jump_size (
                long js
            );
            /*!
                ensures
                    - if (js < 1) then
                        - #jump_size() == 1
                    - else
                        - #jump_size() == js
            !*/

            template <
                typename T
                >
            void set_scroll_handler (
                T& object,
                void (T::*event_handler)()
            );
            /*!
                requires
                    - event_handler is a valid pointer to a member function in T
                ensures
                    - The event_handler function is called whenever the user causes the slider box
                      to move.
                    - This event is NOT triggered by calling set_slider_pos()
                    - any previous calls to this function are overridden by this new call.
                      (i.e. you can only have one event handler associated with this
                      event at a time)
                throws
                    - std::bad_alloc
            !*/

        private:

            // restricted functions
            scroll_bar(scroll_bar&);        // copy constructor
            scroll_bar& operator=(scroll_bar&);    // assignment operator
        };

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // class widget_group
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        class widget_group : public drawable
        {
            /*!
                INITIAL VALUE
                    size() == 0
                    get_rect().is_empty() == true
                    left() == 0
                    top() == 0

                WHAT THIS OBJECT REPRESENTS
                    This object represents a grouping of drawable widgets.  It doesn't draw
                    anything itself, rather it lets you manipulate the position, enabled
                    status, and visibility of a set of widgets as a group.
            !*/

        public:
            widget_group(
                drawable_window& w
            );
            /*!
                ensures
                    - #*this is properly initialized
                    - #*this has been added to window w
                    - #parent_window() == w
                throws
                    - std::bad_alloc
                    - dlib::thread_error
            !*/

            virtual ~widget_group(
            );
            /*!
                ensures
                    - all resources associated with *this have been released.
            !*/

            void empty (
            );
            /*!
                ensures
                    - #size() == 0
            !*/

            void fit_to_contents (
            );
            /*!
                ensures
                    - does not change the position of this object.
                      (i.e. the upper left corner of get_rect() remains at the same position)
                    - if (size() == 0) then
                        - #get_rect().is_empty() == true
                    - else
                        - #get_rect() will be the smallest rectangle that contains all the
                          widgets in this group and the upper left corner of get_rect().
            !*/

            unsigned long size (
            ) const;
            /*!
                ensures
                    - returns the number of widgets currently in *this.
            !*/

            void add (
                drawable& widget,
                unsigned long x,
                unsigned long y
            );
            /*!
                ensures
                    - #is_member(widget) == true
                    - if (is_member(widget) == false) then
                        - #size() == size() + 1
                    - else
                        - #size() == size()
                    - The following conditions apply to this function as well as to all of the
                      following functions so long as is_member(widget) == true:
                      enable(), disable(), hide(), show(), set_z_order(), and set_pos().
                        - #widget.left() == left()+x
                        - #widget.width() == widget.width()
                        - #widget.top() == top()+y
                        - #widget.height() == widget.height()
                        - #widget.is_hidden() == is_hidden()
                        - #widget.is_enabled() == is_enabled()
                        - #widget.z_order() == z_order()
                throws
                    - std::bad_alloc
            !*/

            bool is_member (
                const drawable& widget
            ) const;
            /*!
                ensures
                    - returns true if widget is currently in this object, returns false otherwise.
            !*/

            void remove (
                const drawable& widget
            );
            /*!
                ensures
                    - #is_member(widget) == false
                    - if (is_member(widget) == true) then
                        - #size() == size() - 1
                    - else
                        - #size() == size()
            !*/

        protected:

            // this object doesn't draw anything but also isn't abstract
            void draw (
                const canvas& c
            ) const {}

        private:

            // restricted functions
            widget_group(widget_group&);        // copy constructor
            widget_group& operator=(widget_group&);    // assignment operator
        };

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // class image_widget
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        class image_widget : public draggable
        {
            /*!
                INITIAL VALUE
                    draggable_area() == an initial value for its type.
                    This object isn't displaying anything.

                WHAT THIS OBJECT REPRESENTS
                    This object represents a draggable image.  You give it an image to display
                    by calling set_image().

                    Also note that initially the draggable area is empty so it won't be
                    draggable unless you call set_draggable_area() to some non-empty region.

                    The image is drawn such that:
                        - the pixel img[0][0] is the upper left corner of the image.
                        - the pixel img[img.nr()-1][0] is the lower left corner of the image.
                        - the pixel img[0][img.nc()-1] is the upper right corner of the image.
                        - the pixel img[img.nr()-1][img.nc()-1] is the lower right corner of the image.

            !*/

        public:

            image_widget(
                drawable_window& w
            );
            /*!
                ensures
                    - #*this is properly initialized
                    - #*this has been added to window w
                    - #parent_window() == w
                throws
                    - std::bad_alloc
                    - dlib::thread_error
            !*/

            virtual ~image_widget(
            );
            /*!
                ensures
                    - all resources associated with *this have been released
            !*/

            template <
                typename image_type
                >
            void set_image (
                const image_type& img
            );
            /*!
                requires
                    - image_type == an implementation of array2d/array2d_kernel_abstract.h
                    - pixel_traits<typename image_type::type> must be defined
                ensures
                    - #width() == img.nc()
                    - #height() == img.nr()
                    - #*this widget is now displaying the given image img.
            !*/

        private:

            // restricted functions
            image_widget(image_widget&);        // copy constructor
            image_widget& operator=(image_widget&);    // assignment operator
        };

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // class tooltip
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        class tooltip : public mouse_over_event
        {
            /*!
                INITIAL VALUE
                    - text() == ""
                    - the tooltip is inactive until the text is changed to
                      a non-empty string.

                WHAT THIS OBJECT REPRESENTS
                    This object represents a region on a window where if the user
                    hovers the mouse over this region a tooltip with a message
                    appears.
            !*/

        public:

            tooltip(
                drawable_window& w
            );
            /*!
                ensures
                    - #*this is properly initialized
                    - #*this has been added to window w
                    - #parent_window() == w
                throws
                    - std::bad_alloc
                    - dlib::thread_error
            !*/

            virtual ~tooltip(
            );
            /*!
                ensures
                    - all resources associated with *this have been released
            !*/

            void set_size (
                long width_,
                long height_
            );
            /*!
                ensures
                    - #width() == width_
                    - #height() == height_
                    - #top() == top()
                    - #left() == left()
                    - i.e. The location of the upper left corner of this widget stays the
                      same but its width and height are modified
            !*/

            void set_text (const std::wstring& str);
            void set_text (const dlib::ustring& str);
            void set_text (
                const std::string& str
            );
            /*!
                ensures
                    - #text() == str
                    - activates the tooltip.  i.e. after this function the tooltip
                      will display on the screen when the user hovers the mouse over it
            !*/

            const std::wstring  wtext () const;
            const dlib::ustring utext () const;
            const std::string   text (
            ) const;
            /*!
                ensures
                    - returns the text that is displayed inside this
                      tooltip
            !*/

        private:

            // restricted functions
            tooltip(tooltip&);        // copy constructor
            tooltip& operator=(tooltip&);    // assignment operator
        };

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // popup menu stuff
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        class menu_item
        {
            /*!
                WHAT THIS OBJECT REPRESENTS
                    This is an abstract class that defines the interface a
                    menu item in a popup_menu must implement.

                    Note that a menu_item is drawn as 3 separate pieces:
                        ---------------------------------
                        | left | middle |         right |
                        ---------------------------------

                    Also note that derived classes must be copyable via
                    their copy constructors.
            !*/

        public:

            virtual ~menu_item() {}

            virtual void on_click (
            ) const {}
            /*!
                requires
                    - the mutex drawable::m is locked
                    - if (has_click_event()) then
                        - this function is called when the user clicks on this menu_item
            !*/

            virtual bool has_click_event (
            ) const { return false; }
            /*!
                ensures
                    - if (this menu_item wants to receive on_click events) then
                        - returns true
                    - else
                        - returns false
            !*/

            virtual unichar get_hot_key (
            ) const { return 0; }
            /*!
                ensures
                    - if (this menu item has a keyboard hot key) then
                        - returns the unicode value of the key
                    - else
                        - returns 0
            !*/

            virtual rectangle get_left_size (
            ) const { return rectangle(); } // return empty rect by default
            /*!
                ensures
                    - returns the dimensions of the left part of the menu_item
            !*/

            virtual rectangle get_middle_size (
            ) const = 0;
            /*!
                ensures
                    - returns the dimensions of the middle part of the menu_item
            !*/

            virtual rectangle get_right_size (
            ) const { return rectangle(); } // return empty rect by default
            /*!
                ensures
                    - returns the dimensions of the right part of the menu_item
            !*/

            virtual void draw_background (
                const canvas& c,
                const rectangle& rect,
                const bool enabled,
                const bool is_selected
            ) const {}
            /*!
                requires
                    - the mutex drawable::m is locked
                requires
                    - c == the canvas to draw on
                    - rect == the rectangle in which we are to draw the background
                    - enabled == true if the menu_item is to be drawn enabled
                    - is_selected == true if the menu_item is to be drawn selected
                ensures
                    - draws the background of the menu_item on the canvas c at the location
                      given by rect.
            !*/

            virtual void draw_left (
                const canvas& c,
                const rectangle& rect,
                const bool enabled,
                const bool is_selected
            ) const {}
            /*!
                requires
                    - the mutex drawable::m is locked
                requires
                    - c == the canvas to draw on
                    - rect == the rectangle in which we are to draw the background
                    - enabled == true if the menu_item is to be drawn enabled
                    - is_selected == true if the menu_item is to be drawn selected
                ensures
                    - draws the left part of the menu_item on the canvas c at the location
                      given by rect.
            !*/

            virtual void draw_middle (
                const canvas& c,
                const rectangle& rect,
                const bool enabled,
                const bool is_selected
            ) const = 0;
            /*!
                requires
                    - the mutex drawable::m is locked
                requires
                    - c == the canvas to draw on
                    - rect == the rectangle in which we are to draw the background
                    - enabled == true if the menu_item is to be drawn enabled
                    - is_selected == true if the menu_item is to be drawn selected
                ensures
                    - draws the middle part of the menu_item on the canvas c at the location
                      given by rect.
            !*/

            virtual void draw_right (
                const canvas& c,
                const rectangle& rect,
                const bool enabled,
                const bool is_selected
            ) const {}
            /*!
                requires
                    - the mutex drawable::m is locked
                requires
                    - c == the canvas to draw on
                    - rect == the rectangle in which we are to draw the background
                    - enabled == true if the menu_item is to be drawn enabled
                    - is_selected == true if the menu_item is to be drawn selected
                ensures
                    - draws the right part of the menu_item on the canvas c at the location
                      given by rect.
            !*/
        };

    // ----------------------------------------------------------------------------------------

        class menu_item_text : public menu_item
        {
            /*!
                WHAT THIS OBJECT REPRESENTS
                    This object is a simple text menu item
            !*/

        public:

            template <
                typename T
                >
            menu_item_text (
                const std::string& str,
                T& object,
                void (T::*on_click_handler)(),
                unichar hotkey = 0
            );
            /*!
                ensures
                    - The text of this menu item will be str
                    - the on_click_handler function is called on object when this menu_item
                      clicked by the user.
                    - #get_hot_key() == hotkey
            !*/

            // overloads for wide charactor strings
            template <
                typename T
                >
            menu_item_text (
                const std::wstring& str,
                T& object,
                void (T::*on_click_handler)(),
                unichar hotkey = 0
            );

            template <
                typename T
                >
            menu_item_text (
                const dlib::ustring& str,
                T& object,
                void (T::*on_click_handler)(),
                unichar hotkey = 0
            );
        };

    // ----------------------------------------------------------------------------------------

        class menu_item_submenu : public menu_item
        {
            /*!
                WHAT THIS OBJECT REPRESENTS
                    This object is a simple text item intended to be used with
                    submenus inside a popup_menu.
            !*/

        public:

            menu_item_submenu (
                const std::string& str,
                unichar hotkey = 0
            );
            /*!
                ensures
                    - The text of this menu item will be str
                    - #get_hot_key() == hotkey
            !*/

            //overloads for wide charactor strings
            menu_item_submenu (
                const std::wstring& str,
                unichar hotkey = 0
            );

            menu_item_submenu (
                const dlib::ustring& str,
                unichar hotkey = 0
            );
        };

    // ----------------------------------------------------------------------------------------

        class menu_item_separator : public menu_item
        {
            /*!
                WHAT THIS OBJECT REPRESENTS
                    This object is a horizontal separator in a popup menu
            !*/
        };

    // ----------------------------------------------------------------------------------------

        class popup_menu : public base_window
        {
            /*!
                INITIAL VALUE
                    - size() == 0

                WHAT THIS OBJECT REPRESENTS
                    This object represents a popup menu window capable of containing
                    menu_item objects.
            !*/

        public:

            popup_menu (
            );
            /*!
                ensures
                    - #*this is properly initialized
                throws
                    - std::bad_alloc
                    - dlib::thread_error
                    - dlib::gui_error
            !*/

            void clear (
            );
            /*!
                ensures
                    - #*this has its initial value
                throws
                    - std::bad_alloc
                      if this exception is thrown then *this is unusable
                      until clear() is called and succeeds
            !*/

            template <
                typename menu_item_type
                >
            unsigned long add_menu_item (
                const menu_item_type& new_item
            );
            /*!
                requires
                    - menu_item_type == a type that inherits from menu_item
                ensures
                    - adds new_item onto the bottom of this popup_menu.
                    - returns size()
                      (This is also the index by which this item can be
                      referenced by the enable_menu_item() and disable_menu_item()
                      functions.)
            !*/

            template <
                typename menu_item_type
                >
            unsigned long add_submenu (
                const menu_item_type& new_item,
                popup_menu& submenu
            );
            /*!
                requires
                    - menu_item_type == a type that inherits from menu_item
                ensures
                    - adds new_item onto the bottom of this popup_menu.
                    - when the user puts the mouse above this menu_item the given
                      submenu popup_menu will be displayed.
                    - returns size()
                      (This is also the index by which this item can be
                      referenced by the enable_menu_item() and disable_menu_item()
                      functions.)
            !*/

            void enable_menu_item (
                unsigned long idx
            );
            /*!
                requires
                    - idx < size()
                ensures
                    - the menu_item in this with the index idx has been enabled
            !*/

            void disable_menu_item (
                unsigned long idx
            );
            /*!
                requires
                    - idx < size()
                ensures
                    - the menu_item in this with the index idx has been disabled
            !*/

            unsigned long size (
            ) const;
            /*!
                ensures
                    - returns the number of menu_item objects in this popup_menu
            !*/

            template <typename T>
            void set_on_hide_handler (
                T& object,
                void (T::*event_handler)()
            );
            /*!
                ensures
                    - the event_handler function is called on object when this popup_menu
                      hides itself due to an action by the user.
                    - Note that you can register multiple handlers for this event.
            !*/

            void select_first_item (
            );
            /*!
                ensures
                    - causes this popup menu to highlight the first
                      menu item that it contains which has a click event
                      and is enabled.
            !*/

            bool forwarded_on_keydown (
                unsigned long key,
                bool is_printable,
                unsigned long state
            );
            /*!
                requires
                    - key, is_printable, and state are the variables from the
                      base_window::on_keydown() event
                ensures
                    - forwards this keyboard event to this popup window so that it
                      may deal with keyboard events from other windows.
                    - if (this popup_menu uses the keyboard event) then
                        - returns true
                    - else
                        - returns false
            !*/

        private:

            // restricted functions
            popup_menu(popup_menu&);        // copy constructor
            popup_menu& operator=(popup_menu&);    // assignment operator

        };

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // class popup_menu_region
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        class popup_menu_region : public drawable
        {
            /*!
                WHAT THIS OBJECT REPRESENTS
                    This object represents a region on a window where if the user
                    right clicks the mouse over this region a popup_menu pops up.

                    Note that this widget doesn't actually draw anything, it just
                    provides a region the user can click on to get a popup menu.
            !*/

        public:

            popup_menu_region(
                drawable_window& w
            );
            /*!
                ensures
                    - #*this is properly initialized
                    - #*this has been added to window w
                    - #parent_window() == w
                throws
                    - std::bad_alloc
                    - dlib::thread_error
            !*/

            virtual ~popup_menu_region(
            );
            /*!
                ensures
                    - all resources associated with *this have been released
            !*/

            void set_size (
                long width_,
                long height_
            );
            /*!
                ensures
                    - #width() == width_
                    - #height() == height_
                    - #top() == top()
                    - #left() == left()
                    - i.e. The location of the upper left corner of this widget stays the
                      same but its width and height are modified
            !*/

            popup_menu& menu (
            );
            /*!
                ensures
                    - returns a reference to the popup_menu for this object. It is
                      the menu that is displayed when the user right clicks on
                      this widget
            !*/

        private:

            // restricted functions
            popup_menu_region(popup_menu_region&);        // copy constructor
            popup_menu_region& operator=(popup_menu_region&);    // assignment operator
        };

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // class zoomable_region
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        class zoomable_region : public drawable
        {
            /*
                INITIAL VALUE
                    - min_zoom_scale() == 0.15
                    - max_zoom_scale() == 1.0
                    - zoom_increment() == 0.02
                    - zoom_scale() == 1.0

                WHAT THIS OBJECT REPRESENTS
                    This object represents a 2D Cartesian graph that you can zoom into and
                    out of.  It is a graphical a widget that draws a rectangle with
                    a horizontal and vertical scroll bar that allow the user to scroll
                    around on a Cartesian graph that is much larger than the actual
                    area occupied by this object on the screen.  It also allows
                    the user to zoom in and out.

                    To use this object you inherit from it and make use of its public and
                    protected member functions.  It provides functions for converting between
                    pixel locations and the points in our 2D Cartesian graph so that when the
                    user is scrolling/zooming the widget you can still determine where
                    things are to be placed on the screen and what screen pixels correspond
                    to in the Cartesian graph.

                    Note that the Cartesian graph in this object is bounded by the point
                    (0,0), corresponding to the upper left corner when we are zoomed all
                    the way out, and max_graph_point() which corresponds to the lower right
                    corner when zoomed all the way out. The value of max_graph_point() is
                    determined automatically from the size of this object's on screen
                    rectangle and the value of min_zoom_scale() which determines how far
                    out you can zoom.

                    Also note that while dlib::vector<double> is used to represent graph points
                    the z field is always ignored by this object.
            */

        public:

            zoomable_region (
                drawable_window& w,
                unsigned long events = 0
            );
            /*!
                ensures
                    - #*this is properly initialized
                    - #*this has been added to window w
                    - #parent_window() == w
                    - This object will not receive any events or draw() requests until
                      enable_events() is called
                    - the events flags are passed on to the drawable object's
                      constructor.
                throws
                    - std::bad_alloc
                    - dlib::thread_error
            !*/

            virtual ~zoomable_region (
            ) = 0;
            /*!
                ensures
                    - all resources associated with *this have been released
            !*/

            template <
                typename style_type
                >
            void set_style (
                const style_type& style_
            );
            /*!
                requires
                    - style_type == a type that inherits from scrollable_region_style
                ensures
                    - this zoomable_region object will draw itself using the given
                      style
            !*/

            void set_zoom_increment (
                double zi
            );
            /*!
                ensures
                    - #zoom_increment() == zi
            !*/

            double zoom_increment (
            ) const;
            /*!
                ensures
                    - returns the amount by which zoom_scale() is changed when the user
                      zooms in or out by using the mouse wheel.
            !*/

            void set_max_zoom_scale (
                double ms
            );
            /*!
                ensures
                    - #max_zoom_scale() == ms
            !*/

            void set_min_zoom_scale (
                double ms
            );
            /*!
                ensures
                    - #min_zoom_scale() == ms
            !*/

            double min_zoom_scale (
            ) const;
            /*!
                ensures
                    - returns the minimum allowed value of zoom_scale()
            !*/

            double max_zoom_scale (
            ) const;
            /*!
                ensures
                    - returns the maximum allowed value of zoom_scale()
            !*/

            void set_size (
                long width,
                long height
            );
            /*!
                ensures
                    - #width() == width_
                    - #height() == height_
                    - #top() == top()
                    - #left() == left()
                    - i.e. The location of the upper left corner of this button stays the
                      same but its width and height are modified
            !*/

        protected:

            rectangle display_rect (
            ) const;
            /*!
                requires
                    - mutex drawable::m is locked
                ensures
                    - returns the rectangle on the screen that contains the Cartesian
                      graph in this widget.  I.e. this is the area of this widget minus
                      the area taken up by the scroll bars and border decorations.
            !*/

            point graph_to_gui_space (
                const vector<double>& graph_point
            ) const;
            /*!
                requires
                    - mutex drawable::m is locked
                ensures
                    - returns the location of the pixel on the screen that corresponds
                      to the given point in Cartesian graph space
            !*/

            vector<double> gui_to_graph_space (
                const point& pixel_point
            ) const;
            /*!
                requires
                    - mutex drawable::m is locked
                ensures
                    - returns the point in Cartesian graph space that corresponds to the given
                      pixel location
            !*/

            vector<double> max_graph_point (
            ) const;
            /*!
                requires
                    - mutex drawable::m is locked
                ensures
                    - returns the pixel farthest from the graph point (0,0) that is still
                      in the graph.  I.e. returns the point in graph space that corresponds
                      to the lower right corner of the display_rect() when we are zoomed
                      all the way out.
            !*/

            double zoom_scale (
            ) const;
            /*!
                requires
                    - mutex drawable::m is locked
                ensures
                    - returns a double Z that represents the current zoom.
                        - Smaller values of Z represent the user zooming out.
                        - Bigger values of Z represent the user zooming in.
                        - The default unzoomed case is when Z == 1
                        - objects should be drawn such that they are zoom_scale()
                          times their normal size
            !*/

            void set_zoom_scale (
                double new_scale
            );
            /*!
                requires
                    - mutex drawable::m is locked
                ensures
                    - if (min_zoom_scale() <= new_scale && new_scale <= max_zoom_scale()) then
                        - #zoom_scale() == new_scale
                        - invalidates the display_rect() so that it will be redrawn
                    - else
                       - #zoom_scale() == zoom_scale()
                         I.e. this function has no effect
            !*/

            void center_display_at_graph_point (
                const vector<double>& graph_point
            );
            /*!
                requires
                    - mutex drawable::m is locked
                ensures
                    - causes the given graph point to be centered in the display
                      if possible
                    - invalidates the display_rect() so that it will be redrawn
            !*/

        // ---------------------------- event handlers ----------------------------
        // The following event handlers are used in this object.  So if you
        // use any of them in your derived object you should pass the events
        // back to it so that they still operate unless you wish to hijack the
        // event for your own reasons (e.g. to override the mouse drag this object
        // performs)

            void on_wheel_down (unsigned long state);
            void on_wheel_up (unsigned long state);
            void on_mouse_move ( unsigned long state, long x, long y);
            void on_mouse_up ( unsigned long btn, unsigned long state, long x, long y);
            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:

            // restricted functions
            zoomable_region(zoomable_region&);        // copy constructor
            zoomable_region& operator=(zoomable_region&);    // assignment operator

        };

    // ----------------------------------------------------------------------------------------

        class scrollable_region : public drawable
        {
            /*!
                INITIAL VALUE
                    - horizontal_scroll_pos() == 0
                    - horizontal_scroll_increment() == 1
                    - horizontal_mouse_wheel_scroll_increment() == 1
                    - vertical_scroll_pos() == 0
                    - vertical_scroll_increment() == 1
                    - vertical_mouse_wheel_scroll_increment() == 1
                    - total_rect().empty() == true
                    - mouse_drag_enabled() == false

                WHAT THIS OBJECT REPRESENTS
                    This object represents a 2D region of arbitrary size that is displayed
                    within a possibly smaller scrollable gui widget.  That is, it is a
                    graphical widget that draws a rectangle with a horizontal and vertical
                    scroll bar that allows the user to scroll around on a region that is much
                    larger than the actual area occupied by this object on the screen.

                    To use this object you inherit from it and make use of its public and
                    protected member functions.  It provides a function, total_rect(), that
                    tells you where the 2D region is on the screen.  You draw your stuff
                    inside total_rect() as you would normally except that you only modify
                    pixels that are also inside display_rect().  When the user moves the
                    scroll bars the position of total_rect() is updated accordingly, causing
                    the widget's content to scroll across the screen.
            !*/

        public:
            scrollable_region (
                drawable_window& w,
                unsigned long events = 0
            );
            /*!
                ensures
                    - #*this is properly initialized
                    - #*this has been added to window w
                    - #parent_window() == w
                    - This object will not receive any events or draw() requests until
                      enable_events() is called
                    - the events flags are passed on to the drawable object's
                      constructor.
                throws
                    - std::bad_alloc
                    - dlib::thread_error
            !*/

            virtual ~scrollable_region (
            ) = 0;
            /*!
                ensures
                    - all resources associated with *this have been released
            !*/

            template <
                typename style_type
                >
            void set_style (
                const style_type& style_
            );
            /*!
                requires
                    - style_type == a type that inherits from scrollable_region_style
                ensures
                    - this scrollable_region object will draw itself using the given
                      style
            !*/

            void set_size (
                unsigned long width,
                unsigned long height
            );
            /*!
                ensures
                    - #width() == width_
                    - #height() == height_
                    - #top() == top()
                    - #left() == left()
                    - i.e. The location of the upper left corner of this button stays the
                      same but its width and height are modified
            !*/

            long horizontal_scroll_pos (
            ) const;
            /*!
                ensures
                    - returns the current position of the horizontal scroll bar.
                      0 means it is at the far left while bigger values represent
                      scroll positions closer to the right.
            !*/

            long vertical_scroll_pos (
            ) const;
            /*!
                ensures
                    - returns the current position of the vertical scroll bar.
                      0 means it is at the top and bigger values represent scroll positions
                      closer to the bottom.
            !*/

            void set_horizontal_scroll_pos (
                long pos
            );
            /*!
                ensures
                    - if (pos is a valid horizontal scroll position) then
                        - #horizontal_scroll_pos() == pos
                    - else
                        - #horizontal_scroll_pos() == the valid scroll position closest to pos
            !*/

            void set_vertical_scroll_pos (
                long pos
            );
            /*!
                ensures
                    - if (pos is a valid vertical scroll position) then
                        - #vertical_scroll_pos() == pos
                    - else
                        - #vertical_scroll_pos() == the valid scroll position closest to pos
            !*/

            unsigned long horizontal_mouse_wheel_scroll_increment (
            ) const;
            /*!
                ensures
                    - returns the number of positions the horizontal scroll bar
                      moves when the user scrolls the mouse wheel.
            !*/

            unsigned long vertical_mouse_wheel_scroll_increment (
            ) const;
            /*!
                ensures
                    - returns the number of positions the vertical scroll bar
                      moves when the user scrolls the mouse wheel.
            !*/

            void set_horizontal_mouse_wheel_scroll_increment (
                unsigned long inc
            );
            /*!
                ensures
                    - #horizontal_mouse_wheel_scroll_increment() == inc
            !*/

            void set_vertical_mouse_wheel_scroll_increment (
                unsigned long inc
            );
            /*!
                ensures
                    - #vertical_mouse_wheel_scroll_increment() == inc
            !*/

            unsigned long horizontal_scroll_increment (
            ) const;
            /*!
                ensures
                    - returns the number of pixels that total_rect() is moved by when
                      the horizontal scroll bar moves by one position
            !*/

            unsigned long vertical_scroll_increment (
            ) const;
            /*!
                ensures
                    - returns the number of pixels that total_rect() is moved by when
                      the vertical scroll bar moves by one position
            !*/

            void set_horizontal_scroll_increment (
                unsigned long inc
            );
            /*!
                ensures
                    - #horizontal_scroll_increment() == inc
            !*/

            void set_vertical_scroll_increment (
                unsigned long inc
            );
            /*!
                ensures
                    - #vertical_scroll_increment() == inc
            !*/

            bool mouse_drag_enabled (
            ) const;
            /*!
                ensures
                    - if (the user can drag this contents of this widget around by
                      holding down the left mouse button and dragging) then
                        - returns true
                    - else
                        - returns false
            !*/

            void enable_mouse_drag (
            );
            /*!
                ensures
                    - #mouse_drag_enabled() == true
            !*/

            void disable_mouse_drag (
            );
            /*!
                ensures
                    - #mouse_drag_enabled() == false
            !*/

        protected:

            rectangle display_rect (
            ) const;
            /*!
                requires
                    - mutex drawable::m is locked
                ensures
                    - returns the rectangle on the screen that contains the scrollable
                      area in this widget.  I.e. this is the area of this widget minus
                      the area taken up by the scroll bars and border decorations.
            !*/

            void set_total_rect_size (
                unsigned long width,
                unsigned long height
            );
            /*!
                requires
                    - mutex drawable::m is locked
                    - (width > 0 && height > 0) || (width == 0 && height == 0)
                ensures
                    - #total_rect().width()  == width
                    - #total_rect().height() == height
                    - The scroll bars as well as the position of #total_rect()
                      is updated so that the total rect is still in the correct
                      position with respect to the scroll bars.
            !*/

            const rectangle& total_rect (
            ) const;
            /*!
                requires
                    - mutex drawable::m is locked
                ensures
                    - returns a rectangle that represents the entire scrollable
                      region inside this widget, even the parts that are outside
                      display_rect().
            !*/

            void scroll_to_rect (
                const rectangle& r
            );
            /*!
                requires
                    - mutex drawable::m is locked
                ensures
                    - Adjusts the scroll bars of this object so that the part of
                      the total_rect() rectangle that overlaps with r is displayed in
                      the display_rect() rectangle on the screen.
            !*/

        // ---------------------------- event handlers ----------------------------
        // The following event handlers are used in this object.  So if you
        // use any of them in your derived object you should pass the events
        // back to it so that they still operate unless you wish to hijack the
        // event for your own reasons (e.g. to override the mouse wheel action
        // this object performs)

            void on_wheel_down (unsigned long state);
            void on_wheel_up   (unsigned long state);
            void on_mouse_move (unsigned long state, long x, long y);
            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 draw (const canvas& c) const;

        private:

            // restricted functions
            scrollable_region(scrollable_region&);        // copy constructor
            scrollable_region& operator=(scrollable_region&);    // assignment operator

        };

    // ----------------------------------------------------------------------------------------

    }

    #endif // DLIB_BASE_WIDGETs_ABSTRACT_