sustaining_gazes/lib/3rdParty/dlib/include/dlib/gui_widgets/base_widgets.h

2678 lines
68 KiB
C++

// Copyright (C) 2005 Davis E. King (davis@dlib.net), Keita Mochizuki
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_BASE_WIDGETs_
#define DLIB_BASE_WIDGETs_
#include "base_widgets_abstract.h"
#include "drawable.h"
#include "../gui_core.h"
#include "../algs.h"
#include "../member_function_pointer.h"
#include "../timer.h"
#include "../map.h"
#include "../set.h"
#include "../array2d.h"
#include "../pixel.h"
#include "../image_transforms/assign_image.h"
#include "../array.h"
#include "style.h"
#include "../smart_pointers.h"
#include "../unicode.h"
#include <cctype>
#include "../any.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// class draggable
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
class draggable : public drawable
{
/*!
INITIAL VALUE
- drag == false
CONVENTION
- if (the user is holding the left button down over this object) then
- drag == true
- x == the x position of the mouse relative to the upper left corner
of this object.
- y == the y position of the mouse relative to the upper left corner
of this object.
- else
- drag == false
!*/
public:
draggable(
drawable_window& w,
unsigned long events = 0
) :
drawable(w,events | MOUSE_MOVE | MOUSE_CLICK),
drag(false)
{}
virtual ~draggable(
) = 0;
rectangle draggable_area (
) const { auto_mutex M(m); return area; }
void set_draggable_area (
const rectangle& area_
) { auto_mutex M(m); area = area_; }
protected:
bool is_being_dragged (
) const { return drag; }
virtual void on_drag (
){}
virtual void on_drag_stop (
){}
void on_mouse_move (
unsigned long state,
long x,
long y
);
void on_mouse_down (
unsigned long btn,
unsigned long ,
long x,
long y,
bool
);
void on_mouse_up (
unsigned long btn,
unsigned long state,
long x,
long y
);
private:
rectangle area;
bool drag;
long x, y;
// 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
CONVENTION
- is_mouse_over_ == is_mouse_over()
!*/
public:
mouse_over_event(
drawable_window& w,
unsigned long events = 0
) :
drawable(w,events | MOUSE_MOVE),
is_mouse_over_(false)
{}
virtual ~mouse_over_event(
) = 0;
int next_free_user_event_number() const
{
return drawable::next_free_user_event_number()+1;
}
protected:
bool is_mouse_over (
) const;
virtual void on_mouse_over (
){}
virtual void on_mouse_not_over (
){}
void on_mouse_leave (
);
void on_mouse_move (
unsigned long state,
long x,
long y
);
void on_user_event (
int num
);
private:
mutable bool is_mouse_over_;
// 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
- seen_click == false
CONVENTION
- is_depressed_ == is_depressed()
- if (the user has clicked the button but hasn't yet released the
left mouse button) then
- seen_click == true
- else
- seen_click == false
!*/
public:
button_action(
drawable_window& w,
unsigned long events = 0
) :
mouse_over_event(w,events | MOUSE_MOVE | MOUSE_CLICK),
is_depressed_(false),
seen_click(false)
{}
virtual ~button_action(
) = 0;
int next_free_user_event_number() const
{
return mouse_over_event::next_free_user_event_number()+1;
}
protected:
bool is_depressed (
) const;
virtual void on_button_down (
){}
virtual void on_button_up (
bool
){}
void on_mouse_not_over (
);
void on_mouse_down (
unsigned long btn,
unsigned long ,
long x,
long y,
bool
);
void on_mouse_move (
unsigned long state,
long x,
long y
);
void on_mouse_up (
unsigned long btn,
unsigned long,
long x,
long y
);
private:
mutable bool is_depressed_;
bool seen_click;
void on_user_event (
int num
);
// restricted functions
button_action(button_action&); // copy constructor
button_action& operator=(button_action&); // assignment operator
};
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// class widget_group
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
class widget_group : public drawable
{
/*!
INITIAL VALUE
widgets.size() == 0
CONVENTION
- widgets contains all the drawable objects and their relative positions
that are in *this.
- wg_widgets contains pointers to just the widgets that happen
to be widget_group objects.
!*/
struct relpos
{
unsigned long x;
unsigned long y;
};
public:
widget_group(
drawable_window& w
) : drawable(w) { rect = rectangle(0,0,-1,-1); enable_events();}
virtual ~widget_group(
){ disable_events(); }
void empty (
);
void add (
drawable& widget,
unsigned long x,
unsigned long y
);
void add (
widget_group& widget,
unsigned long x,
unsigned long y
);
bool is_member (
const drawable& widget
) const;
void remove (
const drawable& widget
);
unsigned long size (
) const;
void set_pos (
long x,
long y
);
void set_z_order (
long order
);
void show (
);
void hide (
);
void enable (
);
void disable (
);
void fit_to_contents (
);
protected:
// this object doesn't draw anything but also isn't abstract
void draw (
const canvas&
) const {}
private:
map<drawable*,relpos>::kernel_1a_c widgets;
set<widget_group*>::kernel_1a_c wg_widgets;
// restricted functions
widget_group(widget_group&); // copy constructor
widget_group& operator=(widget_group&); // assignment operator
};
// ----------------------------------------------------------------------------------------
class image_widget : public draggable
{
/*!
INITIAL VALUE
- img.size() == 0
CONVENTION
- img == the image this object displays
!*/
public:
image_widget(
drawable_window& w
): draggable(w) { enable_events(); }
~image_widget(
)
{
disable_events();
parent.invalidate_rectangle(rect);
}
template <
typename image_type
>
void set_image (
const image_type& new_img
)
{
auto_mutex M(m);
assign_image_scaled(img,new_img);
rectangle old(rect);
rect.set_right(rect.left()+img.nc()-1);
rect.set_bottom(rect.top()+img.nr()-1);
parent.invalidate_rectangle(rect+old);
}
private:
void draw (
const canvas& c
) const
{
rectangle area = rect.intersect(c);
if (area.is_empty())
return;
draw_image(c, point(rect.left(),rect.top()), img);
}
array2d<rgb_alpha_pixel> img;
// 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
- stuff.get() == 0
- events_are_enabled() == false
CONVENTION
- if (events_are_enabled() == true) then
- stuff.get() != 0
!*/
public:
tooltip(
drawable_window& w
) :
mouse_over_event(w,MOUSE_CLICK)
{}
~tooltip(
){ disable_events();}
void set_size (
unsigned long width,
unsigned long height
)
{
auto_mutex M(m);
rect = resize_rect(rect,width,height);
}
void set_text (
const std::string& str
)
{
set_text(convert_mbstring_to_wstring(str));
}
void set_text (
const std::wstring& str
)
{
set_text(convert_wstring_to_utf32(str));
}
void set_text (
const ustring& str
)
{
auto_mutex M(m);
if (!stuff)
{
stuff.reset(new data(*this));
enable_events();
}
stuff->win.set_text(str);
}
const std::string text (
) const
{
return convert_wstring_to_mbstring(wtext());
}
const std::wstring wtext (
) const
{
return convert_utf32_to_wstring(utext());
}
const dlib::ustring utext (
) const
{
auto_mutex M(m);
dlib::ustring temp;
if (stuff)
{
temp = stuff->win.text;
}
return temp.c_str();
}
void hide (
)
{
auto_mutex M(m);
mouse_over_event::hide();
if (stuff)
{
stuff->tt_timer.stop();
stuff->win.hide();
}
}
void disable (
)
{
auto_mutex M(m);
mouse_over_event::disable();
if (stuff)
{
stuff->tt_timer.stop();
stuff->win.hide();
}
}
protected:
void on_mouse_over()
{
stuff->x = lastx;
stuff->y = lasty;
stuff->tt_timer.start();
}
void on_mouse_not_over ()
{
stuff->tt_timer.stop();
stuff->win.hide();
}
void on_mouse_down (
unsigned long btn,
unsigned long state,
long x,
long y,
bool is_double_click
)
{
mouse_over_event::on_mouse_down(btn,state,x,y,is_double_click);
stuff->tt_timer.stop();
stuff->win.hide();
}
void draw (
const canvas&
) const{}
private:
class tooltip_window : public base_window
{
public:
tooltip_window (const shared_ptr_thread_safe<font>& f) : base_window(false,true), pad(3), mfont(f)
{
}
ustring text;
rectangle rect_all;
rectangle rect_text;
const unsigned long pad;
const shared_ptr_thread_safe<font> mfont;
void set_text (
const std::string& str
)
{
set_text(convert_mbstring_to_wstring(str));
}
void set_text (
const std::wstring& str
)
{
set_text(convert_wstring_to_utf32(str));
}
void set_text (
const dlib::ustring& str
)
{
text = str.c_str();
unsigned long width, height;
mfont->compute_size(text,width,height);
set_size(width+pad*2, height+pad*2);
rect_all.set_left(0);
rect_all.set_top(0);
rect_all.set_right(width+pad*2-1);
rect_all.set_bottom(height+pad*2-1);
rect_text = move_rect(rectangle(width,height),pad,pad);
}
void paint(const canvas& c)
{
c.fill(255,255,150);
draw_rectangle(c, rect_all);
mfont->draw_string(c,rect_text,text);
}
};
void show_tooltip (
)
{
auto_mutex M(m);
long x, y;
// if the mouse has moved since we started the timer then
// keep waiting until the user stops moving it
if (lastx != stuff->x || lasty != stuff->y)
{
stuff->x = lastx;
stuff->y = lasty;
return;
}
unsigned long display_width, display_height;
// stop the timer
stuff->tt_timer.stop();
parent.get_pos(x,y);
x += lastx+15;
y += lasty+15;
// make sure the tooltip isn't going to be off the screen
parent.get_display_size(display_width, display_height);
rectangle wrect(move_rect(stuff->win.rect_all,x,y));
rectangle srect(display_width, display_height);
if (srect.contains(wrect) == false)
{
rectangle temp(srect.intersect(wrect));
x -= wrect.width()-temp.width();
y -= wrect.height()-temp.height();
}
stuff->win.set_pos(x,y);
stuff->win.show();
}
// put all this stuff in data so we can arrange to only
// construct it when someone is actually using the tooltip widget
// rather than just instantiating it.
struct data
{
data(
tooltip& self
) :
x(-1),
y(-1),
win(self.mfont),
tt_timer(self,&tooltip::show_tooltip)
{
tt_timer.set_delay_time(400);
}
long x, y;
tooltip_window win;
timer<tooltip> tt_timer;
};
friend struct data;
scoped_ptr<data> stuff;
// restricted functions
tooltip(tooltip&); // copy constructor
tooltip& operator=(tooltip&); // assignment operator
};
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// class button
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
class button : public button_action
{
public:
button(
drawable_window& w
) :
button_action(w),
btn_tooltip(w)
{
style.reset(new button_style_default());
enable_events();
}
~button() { disable_events(); parent.invalidate_rectangle(style->get_invalidation_rect(rect)); }
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 set_tooltip_text (
const std::string& text
);
void set_tooltip_text (
const std::wstring& text
);
void set_tooltip_text (
const dlib::ustring& text
);
void set_pos(
long x,
long y
);
const std::string tooltip_text (
) const;
const std::wstring tooltip_wtext (
) const;
const dlib::ustring tooltip_utext (
) const;
void set_main_font (
const shared_ptr_thread_safe<font>& f
);
void show (
);
void hide (
);
void enable (
);
void disable (
);
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(style->get_invalidation_rect(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_)(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(button&)>& event_handler_
)
{
auto_mutex M(m);
event_handler_self = event_handler_;
event_handler.clear();
}
bool is_depressed (
) const
{
auto_mutex M(m);
return button_action::is_depressed();
}
template <
typename T
>
void set_button_down_handler (
T& object,
void (T::*event_handler)()
)
{
auto_mutex M(m);
button_down_handler = make_mfp(object,event_handler);
}
void set_button_down_handler (
const any_function<void()>& event_handler
)
{
auto_mutex M(m);
button_down_handler = event_handler;
}
template <
typename T
>
void set_button_up_handler (
T& object,
void (T::*event_handler)(bool mouse_over)
)
{
auto_mutex M(m);
button_up_handler = make_mfp(object,event_handler);
}
void set_button_up_handler (
const any_function<void(bool)>& event_handler
)
{
auto_mutex M(m);
button_up_handler = event_handler;
}
template <
typename T
>
void set_button_down_handler (
T& object,
void (T::*event_handler)(button&)
)
{
auto_mutex M(m);
button_down_handler_self = make_mfp(object,event_handler);
}
void set_sourced_button_down_handler (
const any_function<void(button&)>& event_handler
)
{
auto_mutex M(m);
button_down_handler_self = event_handler;
}
template <
typename T
>
void set_button_up_handler (
T& object,
void (T::*event_handler)(bool mouse_over, button&)
)
{
auto_mutex M(m);
button_up_handler_self = make_mfp(object,event_handler);
}
void set_sourced_button_up_handler (
const any_function<void(bool,button&)>& event_handler
)
{
auto_mutex M(m);
button_up_handler_self = event_handler;
}
private:
// restricted functions
button(button&); // copy constructor
button& operator=(button&); // assignment operator
dlib::ustring name_;
tooltip btn_tooltip;
any_function<void()> event_handler;
any_function<void(button&)> event_handler_self;
any_function<void()> button_down_handler;
any_function<void(bool)> button_up_handler;
any_function<void(button&)> button_down_handler_self;
any_function<void(bool,button&)> button_up_handler_self;
scoped_ptr<button_style> style;
protected:
void draw (
const canvas& c
) const { style->draw_button(c,rect,enabled,*mfont,lastx,lasty,name_,is_depressed()); }
void on_button_up (
bool mouse_over
);
void on_button_down (
);
void on_mouse_over (
){ if (style->redraw_on_mouse_over()) parent.invalidate_rectangle(style->get_invalidation_rect(rect)); }
void on_mouse_not_over (
){ if (style->redraw_on_mouse_over()) parent.invalidate_rectangle(style->get_invalidation_rect(rect)); }
};
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// class scroll_bar
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
class scroll_bar : public drawable
{
/*!
INITIAL VALUE
- ori == a value given by the constructor
- style == a scroll_bar_style_default object
- pos == 0
- max_pos == 0
- js == 10
CONVENTION
- ori == orientation()
- b1 == the button that is near the 0 end of the scroll bar
- b2 == the button that is near the max_pos() end of the scroll bar
- max_pos == max_slider_pos()
- pos == slider_pos()
- js == jump_size()
!*/
public:
enum bar_orientation
{
HORIZONTAL,
VERTICAL
};
scroll_bar(
drawable_window& w,
bar_orientation orientation_
);
virtual ~scroll_bar(
);
bar_orientation orientation (
) const;
void set_length (
unsigned long length
);
long max_slider_pos (
) const;
void set_max_slider_pos (
long mpos
);
void set_slider_pos (
long pos
);
long slider_pos (
) const;
template <
typename T
>
void set_scroll_handler (
T& object,
void (T::*eh)()
) { auto_mutex M(m); scroll_handler = make_mfp(object,eh); }
void set_scroll_handler (
const any_function<void()>& eh
) { auto_mutex M(m); scroll_handler = eh; }
void set_pos (
long x,
long y
);
void enable (
)
{
auto_mutex M(m);
if (!hidden)
show_slider();
if (max_pos != 0)
{
b1.enable();
b2.enable();
}
drawable::enable();
}
void disable (
)
{
auto_mutex M(m);
hide_slider();
b1.disable();
b2.disable();
drawable::disable();
}
void hide (
)
{
auto_mutex M(m);
hide_slider();
top_filler.hide();
bottom_filler.hide();
b1.hide();
b2.hide();
drawable::hide();
}
void show (
)
{
auto_mutex M(m);
b1.show();
b2.show();
drawable::show();
top_filler.show();
if (enabled)
show_slider();
}
void set_z_order (
long order
)
{
auto_mutex M(m);
slider.set_z_order(order);
top_filler.set_z_order(order);
bottom_filler.set_z_order(order);
b1.set_z_order(order);
b2.set_z_order(order);
drawable::set_z_order(order);
}
void set_jump_size (
long js
);
long jump_size (
) const;
template <
typename style_type
>
void set_style (
const style_type& style_
)
{
auto_mutex M(m);
style.reset(new style_type(style_));
if (ori == HORIZONTAL)
{
b1.set_style(style_.get_left_button_style());
b2.set_style(style_.get_right_button_style());
set_length(rect.width());
}
else
{
b1.set_style(style_.get_up_button_style());
b2.set_style(style_.get_down_button_style());
set_length(rect.height());
}
}
private:
void hide_slider (
);
/*!
ensures
- hides the slider and makes any other changes needed so that the
scroll_bar still looks right.
!*/
void show_slider (
);
/*!
ensures
- shows the slider and makes any other changes needed so that the
scroll_bar still looks right.
!*/
void on_slider_drag (
);
/*!
requires
- is called whenever the user drags the slider
!*/
void draw (
const canvas& c
) const;
void b1_down (
);
void b1_up (
bool mouse_over
);
void b2_down (
);
void b2_up (
bool mouse_over
);
void top_filler_down (
);
void top_filler_up (
bool mouse_over
);
void bottom_filler_down (
);
void bottom_filler_up (
bool mouse_over
);
void on_user_event (
int i
);
void delayed_set_slider_pos (
unsigned long dpos
);
void b1_down_t (
);
void b2_down_t (
);
void top_filler_down_t (
);
void bottom_filler_down_t (
);
friend class filler;
class filler : public button_action
{
friend class scroll_bar;
public:
filler (
drawable_window& w,
scroll_bar& object,
void (scroll_bar::*down)(),
void (scroll_bar::*up)(bool)
):
button_action(w),
my_scroll_bar(object)
{
bup = make_mfp(object,up);
bdown = make_mfp(object,down);
enable_events();
}
~filler (
)
{
disable_events();
}
void set_size (
unsigned long width,
unsigned long height
)
{
rectangle old(rect);
const unsigned long x = rect.left();
const unsigned long y = rect.top();
rect.set_right(x+width-1);
rect.set_bottom(y+height-1);
parent.invalidate_rectangle(rect+old);
}
private:
void draw (
const canvas& c
) const
{
my_scroll_bar.style->draw_scroll_bar_background(c,rect,enabled,lastx,lasty,is_depressed());
}
void on_button_down (
) { bdown(); }
void on_button_up (
bool mouse_over
) { bup(mouse_over); }
scroll_bar& my_scroll_bar;
any_function<void()> bdown;
any_function<void(bool)> bup;
};
friend class slider_class;
class slider_class : public draggable
{
friend class scroll_bar;
public:
slider_class (
drawable_window& w,
scroll_bar& object,
void (scroll_bar::*handler)()
) :
draggable(w, MOUSE_MOVE),
mouse_in_widget(false),
my_scroll_bar(object)
{
callback = make_mfp(object,handler);
enable_events();
}
~slider_class (
)
{
disable_events();
}
void set_size (
unsigned long width,
unsigned long height
)
{
rectangle old(rect);
const unsigned long x = rect.left();
const unsigned long y = rect.top();
rect.set_right(x+width-1);
rect.set_bottom(y+height-1);
parent.invalidate_rectangle(rect+old);
}
private:
virtual void on_mouse_move (
unsigned long state,
long x,
long y
)
{
draggable::on_mouse_move(state,x,y);
if (!hidden && my_scroll_bar.style->redraw_on_mouse_over_slider())
{
if (rect.contains(x,y) && !mouse_in_widget)
{
mouse_in_widget = true;
parent.invalidate_rectangle(rect);
}
else if (rect.contains(x,y) == false && mouse_in_widget)
{
mouse_in_widget = false;
parent.invalidate_rectangle(rect);
}
}
}
void on_mouse_leave (
)
{
if (mouse_in_widget && my_scroll_bar.style->redraw_on_mouse_over_slider())
{
mouse_in_widget = false;
parent.invalidate_rectangle(rect);
}
}
void on_drag_stop (
)
{
if (my_scroll_bar.style->redraw_on_mouse_over_slider())
parent.invalidate_rectangle(rect);
}
void on_drag (
)
{
callback();
}
void draw (
const canvas& c
) const
{
my_scroll_bar.style->draw_scroll_bar_slider(c,rect,enabled,lastx,lasty, is_being_dragged());
}
bool mouse_in_widget;
scroll_bar& my_scroll_bar;
any_function<void()> callback;
};
void adjust_fillers (
);
/*!
ensures
- top_filler and bottom_filler appear in their correct positions
relative to the current positions of the slider and the b1 and
b2 buttons
!*/
unsigned long get_slider_size (
) const;
/*!
ensures
- returns the length in pixels the slider should have based on the current
state of this scroll bar
!*/
button b1, b2;
slider_class slider;
bar_orientation ori;
filler top_filler, bottom_filler;
any_function<void()> scroll_handler;
long pos;
long max_pos;
long js;
timer<scroll_bar> b1_timer;
timer<scroll_bar> b2_timer;
timer<scroll_bar> top_filler_timer;
timer<scroll_bar> bottom_filler_timer;
long delayed_pos;
scoped_ptr<scroll_bar_style> style;
// restricted functions
scroll_bar(scroll_bar&); // copy constructor
scroll_bar& operator=(scroll_bar&); // assignment operator
};
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// class popup_menu
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
class menu_item
{
public:
virtual ~menu_item() {}
virtual rectangle get_left_size (
) const { return rectangle(); }
virtual rectangle get_middle_size (
) const = 0;
virtual rectangle get_right_size (
) const { return rectangle(); }
virtual unichar get_hot_key (
) const { return 0; }
virtual void draw_background (
const canvas& ,
const rectangle& ,
const bool ,
const bool
) const {}
virtual void draw_left (
const canvas& ,
const rectangle& ,
const bool ,
const bool
) const {}
virtual void draw_middle (
const canvas& ,
const rectangle& ,
const bool ,
const bool
) const = 0;
virtual void draw_right (
const canvas& ,
const rectangle& ,
const bool ,
const bool
) const {}
virtual void on_click (
) const {}
virtual bool has_click_event (
) const { return false; }
};
// ----------------------------------------------------------------------------------------
class menu_item_submenu : public menu_item
{
void initialize (
unichar hk
)
{
const dlib::ustring &str = text;
if (hk != 0)
{
std::string::size_type pos = str.find_first_of(hk);
if (pos != std::string::npos)
{
// now compute the location of the underline bar
rectangle r1 = f->compute_cursor_rect( rectangle(100000,100000), str, pos);
rectangle r2 = f->compute_cursor_rect( rectangle(100000,100000), str, pos+1);
underline_p1.x() = r1.left()+1;
underline_p2.x() = r2.left()-1;
underline_p1.y() = r1.bottom()-f->height()+f->ascender()+2;
underline_p2.y() = r2.bottom()-f->height()+f->ascender()+2;
}
}
}
public:
menu_item_submenu (
const std::string& str,
unichar hk = 0
) :
text(convert_wstring_to_utf32(convert_mbstring_to_wstring(str))),
f(default_font::get_font()),
hotkey(hk)
{
initialize(hk);
}
menu_item_submenu (
const std::wstring& str,
unichar hk = 0
) :
text(convert_wstring_to_utf32(str)),
f(default_font::get_font()),
hotkey(hk)
{
initialize(hk);
}
menu_item_submenu (
const dlib::ustring& str,
unichar hk = 0
) :
text(str),
f(default_font::get_font()),
hotkey(hk)
{
initialize(hk);
}
virtual unichar get_hot_key (
) const { return hotkey; }
virtual rectangle get_middle_size (
) const
{
unsigned long width, height;
f->compute_size(text,width,height);
return rectangle(width+30,height);
}
virtual rectangle get_right_size (
) const
{
return rectangle(15, 5);
}
virtual void draw_background (
const canvas& c,
const rectangle& rect,
const bool enabled,
const bool is_selected
) const
{
if (c.intersect(rect).is_empty())
return;
if (enabled && is_selected)
{
fill_rect_with_vertical_gradient(c, rect,rgb_alpha_pixel(0,200,0,100), rgb_alpha_pixel(0,0,0,100));
draw_rectangle(c, rect,rgb_alpha_pixel(0,0,0,100));
}
}
virtual void draw_right (
const canvas& c,
const rectangle& rect,
const bool enabled,
const bool
) const
{
if (c.intersect(rect).is_empty())
return;
unsigned char color = 0;
if (enabled == false)
color = 128;
long x, y;
x = rect.right() - 7;
y = rect.top() + rect.height()/2;
for ( unsigned long i = 0; i < 5; ++i)
draw_line (c, point(x - i, y + i), point(x - i, y - i), color);
}
virtual void draw_middle (
const canvas& c,
const rectangle& rect,
const bool enabled,
const bool
) const
{
if (c.intersect(rect).is_empty())
return;
if (enabled)
{
f->draw_string(c,rect,text);
}
else
{
f->draw_string(c,rect,text,128);
}
if (underline_p1 != underline_p2)
{
point base(rect.left(),rect.top());
draw_line(c, base+underline_p1, base+underline_p2);
}
}
private:
dlib::ustring text;
const shared_ptr_thread_safe<font> f;
any_function<void()> action;
unichar hotkey;
point underline_p1;
point underline_p2;
};
// ----------------------------------------------------------------------------------------
class menu_item_text : public menu_item
{
void initialize (
const any_function<void()>& event_handler_,
unichar hk
)
{
dlib::ustring &str = text;
action = event_handler_;
if (hk != 0)
{
std::string::size_type pos = str.find_first_of(hk);
if (pos != std::string::npos)
{
// now compute the location of the underline bar
rectangle r1 = f->compute_cursor_rect( rectangle(100000,100000), str, pos);
rectangle r2 = f->compute_cursor_rect( rectangle(100000,100000), str, pos+1);
underline_p1.x() = r1.left()+1;
underline_p2.x() = r2.left()-1;
underline_p1.y() = r1.bottom()-f->height()+f->ascender()+2;
underline_p2.y() = r2.bottom()-f->height()+f->ascender()+2;
}
}
}
public:
template <typename T>
menu_item_text (
const std::string& str,
T& object,
void (T::*event_handler_)(),
unichar hk = 0
) :
text(convert_wstring_to_utf32(convert_mbstring_to_wstring(str))),
f(default_font::get_font()),
hotkey(hk)
{
initialize(make_mfp(object, event_handler_), hk);
}
menu_item_text (
const std::string& str,
const any_function<void()>& event_handler_,
unichar hk = 0
) :
text(convert_wstring_to_utf32(convert_mbstring_to_wstring(str))),
f(default_font::get_font()),
hotkey(hk)
{
initialize(event_handler_, hk);
}
template <typename T>
menu_item_text (
const std::wstring& str,
T& object,
void (T::*event_handler_)(),
unichar hk = 0
) :
text(convert_wstring_to_utf32(str)),
f(default_font::get_font()),
hotkey(hk)
{
initialize(make_mfp(object, event_handler_), hk);
}
menu_item_text (
const std::wstring& str,
const any_function<void()>& event_handler_,
unichar hk = 0
) :
text(convert_wstring_to_utf32(str)),
f(default_font::get_font()),
hotkey(hk)
{
initialize(event_handler_, hk);
}
template <typename T>
menu_item_text (
const dlib::ustring& str,
T& object,
void (T::*event_handler_)(),
unichar hk = 0
) :
text(str),
f(default_font::get_font()),
hotkey(hk)
{
initialize(make_mfp(object, event_handler_), hk);
}
menu_item_text (
const dlib::ustring& str,
const any_function<void()>& event_handler_,
unichar hk = 0
) :
text(str),
f(default_font::get_font()),
hotkey(hk)
{
initialize(event_handler_, hk);
}
virtual unichar get_hot_key (
) const { return hotkey; }
virtual rectangle get_middle_size (
) const
{
unsigned long width, height;
f->compute_size(text,width,height);
return rectangle(width,height);
}
virtual void draw_background (
const canvas& c,
const rectangle& rect,
const bool enabled,
const bool is_selected
) const
{
if (c.intersect(rect).is_empty())
return;
if (enabled && is_selected)
{
fill_rect_with_vertical_gradient(c, rect,rgb_alpha_pixel(0,200,0,100), rgb_alpha_pixel(0,0,0,100));
draw_rectangle(c, rect,rgb_alpha_pixel(0,0,0,100));
}
}
virtual void draw_middle (
const canvas& c,
const rectangle& rect,
const bool enabled,
const bool
) const
{
if (c.intersect(rect).is_empty())
return;
unsigned char color = 0;
if (enabled == false)
color = 128;
f->draw_string(c,rect,text,color);
if (underline_p1 != underline_p2)
{
point base(rect.left(),rect.top());
draw_line(c, base+underline_p1, base+underline_p2, color);
}
}
virtual void on_click (
) const
{
action();
}
virtual bool has_click_event (
) const { return true; }
private:
dlib::ustring text;
const shared_ptr_thread_safe<font> f;
any_function<void()> action;
unichar hotkey;
point underline_p1;
point underline_p2;
};
// ----------------------------------------------------------------------------------------
class menu_item_separator : public menu_item
{
public:
virtual rectangle get_middle_size (
) const
{
return rectangle(10,4);
}
virtual void draw_background (
const canvas& c,
const rectangle& rect,
const bool ,
const bool
) const
{
if (c.intersect(rect).is_empty())
return;
point p1(rect.left(),rect.top()+rect.height()/2-1);
point p2(rect.right(),rect.top()+rect.height()/2-1);
point p3(rect.left(),rect.top()+rect.height()/2);
point p4(rect.right(),rect.top()+rect.height()/2);
draw_line(c, p1,p2,128);
draw_line(c, p3,p4,255);
}
virtual void draw_middle (
const canvas& ,
const rectangle& ,
const bool ,
const bool
) const
{
}
};
// ----------------------------------------------------------------------------------------
class popup_menu : public base_window
{
/*!
INITIAL VALUE
- pad == 2
- item_pad == 3
- cur_rect == rectangle(pad,pad,pad-1,pad-1)
- left_width == 0
- middle_width == 0
- selected_item == 0
- submenu_open == false
- items.size() == 0
- item_enabled.size() == 0
- left_rects.size() == 0
- middle_rects.size() == 0
- right_rects.size() == 0
- line_rects.size() == 0
- submenus.size() == 0
- hide_handlers.size() == 0
CONVENTION
- pad = 2
- item_pad = 3
- all of the following arrays have the same size:
- items.size()
- item_enabled.size()
- left_rects.size()
- middle_rects.size()
- right_rects.size()
- line_rects.size()
- submenus.size()
- win_rect == a rectangle that is the exact size of this window and with
its upper left corner at (0,0)
- cur_rect == the rect inside which all the menu items are drawn
- if (a menu_item is supposed to be selected) then
- selected_item == the index in menus of the menu_item
- else
- selected_item == submenus.size()
- if (there is a selected submenu and it is currently open) then
- submenu_open == true
- else
- submenu_open == false
- for all valid i:
- items[i] == a pointer to the ith menu_item
- item_enabled[i] == true if the ith menu_item is enabled, false otherwise
- left_rects[i] == the left rectangle for the ith menu item
- middle_rects[i] == the middle rectangle for the ith menu item
- right_rects[i] == the right rectangle for the ith menu item
- line_rects[i] == the rectangle for the entire line on which the ith menu
item appears.
- if (submenus[i] != 0) then
- the ith menu item has a submenu and it is pointed to by submenus[i]
- hide_handlers == an array of all the on_hide events registered for
this popup_menu
!*/
public:
popup_menu (
);
template <
typename menu_item_type
>
unsigned long add_menu_item (
const menu_item_type& new_item
)
{
auto_mutex M(wm);
bool t = true;
scoped_ptr<menu_item> item(new menu_item_type(new_item));
items.push_back(item);
item_enabled.push_back(t);
// figure out how big the window should be now and what not
rectangle left = new_item.get_left_size();
rectangle middle = new_item.get_middle_size();
rectangle right = new_item.get_right_size();
bool recalc_rect_positions = false;
const rectangle all = left+middle+right;
// make sure left_width contains the max of all the left rectangles
if (left.width() > left_width)
{
left_width = left.width();
recalc_rect_positions = true;
}
// make sure middle_width contains the max of all the middle rectangles
if (middle.width() > middle_width)
{
middle_width = middle.width();
recalc_rect_positions = true;
}
// make the current rectangle wider if necessary
if (cur_rect.width() < left_width + middle_width + right.width() + 2*item_pad)
{
cur_rect = resize_rect_width(cur_rect, left_width + middle_width + right.width() + 2*item_pad);
recalc_rect_positions = true;
}
const long y = cur_rect.bottom()+1 + item_pad;
const long x = cur_rect.left() + item_pad;
// make the current rectangle taller to account for this new menu item
cur_rect.set_bottom(cur_rect.bottom()+all.height() + 2*item_pad);
// adjust all the saved rectangles since the width of the window changed
// or left_width changed
if (recalc_rect_positions)
{
long y = cur_rect.top() + item_pad;
for (unsigned long i = 0; i < left_rects.size(); ++i)
{
middle_rects[i] = move_rect(middle_rects[i], x+left_width, y);
right_rects[i] = move_rect(right_rects[i], x+cur_rect.width()-right_rects[i].width()-item_pad, y);
line_rects[i] = resize_rect_width(line_rects[i], cur_rect.width());
y += line_rects[i].height();
}
}
// save the rectangles for later use. Also position them at the
// right spots
left = move_rect(left,x,y);
middle = move_rect(middle,x+left_width,y);
right = move_rect(right,x+cur_rect.width()-right.width()-item_pad,y);
rectangle line(move_rect(rectangle(cur_rect.width(),all.height()+2*item_pad), x-item_pad, y-item_pad));
// make sure the left, middle, and right rectangles are centered in the
// line.
if (left.height() < all.height())
left = translate_rect(left,0, (all.height()-left.height())/2);
if (middle.height() < all.height())
middle = translate_rect(middle,0, (all.height()-middle.height())/2);
if (right.height() < all.height())
right = translate_rect(right,0, (all.height()-right.height())/2);
left_rects.push_back(left);
middle_rects.push_back(middle);
right_rects.push_back(right);
line_rects.push_back(line);
popup_menu* junk = 0;
submenus.push_back(junk);
win_rect.set_right(cur_rect.right()+pad);
win_rect.set_bottom(cur_rect.bottom()+pad);
set_size(win_rect.width(),win_rect.height());
// make it so that nothing is selected
selected_item = submenus.size();
return items.size()-1;
}
template <
typename menu_item_type
>
unsigned long add_submenu (
const menu_item_type& new_item,
popup_menu& submenu
)
{
auto_mutex M(wm);
submenus[add_menu_item(new_item)] = &submenu;
submenu.set_on_hide_handler(*this,&popup_menu::on_submenu_hide);
return items.size()-1;
}
void enable_menu_item (
unsigned long idx
);
void disable_menu_item (
unsigned long idx
);
unsigned long size (
) const;
void clear (
);
void show (
);
void hide (
);
template <typename T>
void set_on_hide_handler (
T& object,
void (T::*event_handler)()
)
{
auto_mutex M(wm);
member_function_pointer<> temp;
temp.set(object,event_handler);
// if this handler isn't already registered then add it
bool found_handler = false;
for (unsigned long i = 0; i < hide_handlers.size(); ++i)
{
if (hide_handlers[i] == temp)
{
found_handler = true;
break;
}
}
if (found_handler == false)
{
hide_handlers.push_back(temp);
}
}
void select_first_item (
);
bool forwarded_on_keydown (
unsigned long key,
bool is_printable,
unsigned long state
);
private:
void on_submenu_hide (
);
void on_window_resized(
);
void on_mouse_up (
unsigned long btn,
unsigned long,
long x,
long y
);
void on_mouse_move (
unsigned long state,
long x,
long y
);
void close_submenu (
);
bool display_selected_submenu (
);
/*!
ensures
- if (submenus[selected_item] isn't null) then
- displays the selected submenu
- returns true
- else
- returns false
!*/
void on_mouse_leave (
);
void paint (
const canvas& c
);
const long pad;
const long item_pad;
rectangle cur_rect;
rectangle win_rect;
unsigned long left_width;
unsigned long middle_width;
array<scoped_ptr<menu_item> > items;
array<bool> item_enabled;
array<rectangle> left_rects;
array<rectangle> middle_rects;
array<rectangle> right_rects;
array<rectangle> line_rects;
array<popup_menu*> submenus;
unsigned long selected_item;
bool submenu_open;
array<member_function_pointer<> > hide_handlers;
// restricted functions
popup_menu(popup_menu&); // copy constructor
popup_menu& operator=(popup_menu&); // assignment operator
};
// ----------------------------------------------------------------------------------------
class zoomable_region : public drawable
{
/*!
INITIAL VALUE
- min_scale == 0.15
- max_scale == 1.0
- zoom_increment_ == 0.02
- scale == 1.0
- mouse_drag_screen == false
CONVENTION
- zoom_increment() == zoom_increment_
- min_zoom_scale() == min_scale
- max_zoom_scale() == max_scale
- zoom_scale() == scale
- if (the user is currently dragging the graph around via the mouse) then
- mouse_drag_screen == true
- else
- mouse_drag_screen == false
- max_graph_point() == lr_point
- display_rect() == display_rect_
- gui_to_graph_space(point(display_rect.left(),display_rect.top())) == gr_orig
!*/
public:
zoomable_region (
drawable_window& w,
unsigned long events = 0
);
virtual ~zoomable_region (
)= 0;
virtual 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_));
hsb.set_style(style_.get_horizontal_scroll_bar_style());
vsb.set_style(style_.get_vertical_scroll_bar_style());
// do this just so that everything gets redrawn right
set_size(rect.width(), rect.height());
}
void set_zoom_increment (
double zi
);
double zoom_increment (
) const;
void set_max_zoom_scale (
double ms
);
void set_min_zoom_scale (
double ms
);
double min_zoom_scale (
) const;
double max_zoom_scale (
) const;
virtual void set_size (
unsigned long width,
unsigned long height
);
void show (
);
void hide (
);
void enable (
);
void disable (
);
void set_z_order (
long order
);
protected:
virtual void on_view_changed () {}
point graph_to_gui_space (
const vector<double,2>& p
) const;
vector<double,2> gui_to_graph_space (
const point& p
) const;
point max_graph_point (
) const;
rectangle display_rect (
) const;
double zoom_scale (
) const;
void set_zoom_scale (
double new_scale
);
void center_display_at_graph_point (
const vector<double,2>& p
);
// ----------- event handlers ---------------
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:
void on_h_scroll (
);
void on_v_scroll (
);
void redraw_graph (
);
void adjust_origin (
const point& gui_p,
const vector<double,2>& graph_p
);
/*!
ensures
- adjusts gr_orig so that we are as close to the following as possible:
- graph_to_gui_space(graph_p) == gui_p
- gui_to_graph_space(gui_p) == graph_p
!*/
vector<double,2> gr_orig; // point in graph space such that it's gui space point is the upper left of display_rect_
vector<double,2> lr_point; // point in graph space such that it is at the lower right corner of the screen at max zoom
mutable std::ostringstream sout;
double scale; // 0 < scale <= 1
double min_scale;
double max_scale;
double zoom_increment_;
rectangle display_rect_;
bool mouse_drag_screen; // true if the user is dragging the white background area
vector<double,2> drag_screen_point; // the starting point the mouse was at in graph space for the background area drag
scroll_bar vsb;
scroll_bar hsb;
scoped_ptr<scrollable_region_style> style;
// restricted functions
zoomable_region(zoomable_region&); // copy constructor
zoomable_region& operator=(zoomable_region&); // assignment operator
};
// ----------------------------------------------------------------------------------------
class scrollable_region : public drawable
{
/*!
INITIAL VALUE
- hscroll_bar_inc == 1
- vscroll_bar_inc == 1
- h_wheel_scroll_bar_inc == 1
- v_wheel_scroll_bar_inc == 1
- mouse_drag_enabled_ == false
- user_is_dragging_mouse == false
CONVENTION
- mouse_drag_enabled() == mouse_drag_enabled_
- horizontal_scroll_increment() == hscroll_bar_inc
- vertical_scroll_increment() == vscroll_bar_inc
- horizontal_mouse_wheel_scroll_increment() == h_wheel_scroll_bar_inc
- vertical_mouse_wheel_scroll_increment() == v_wheel_scroll_bar_inc
- vertical_scroll_pos() == vsb.slider_pos()
- horizontal_scroll_pos() == hsb.slider_pos()
- total_rect() == total_rect_
- display_rect() == display_rect_
- if (the user is currently dragging the total_rect around with a mouse drag) then
- user_is_dragging_mouse == true
- drag_origin == the point the mouse was at, with respect to total_rect,
when the dragging started
- else
- user_is_dragging_mouse == false
!*/
public:
scrollable_region (
drawable_window& w,
unsigned long events = 0
);
virtual ~scrollable_region (
) = 0;
template <
typename style_type
>
void set_style (
const style_type& style_
)
{
auto_mutex M(m);
style.reset(new style_type(style_));
hsb.set_style(style_.get_horizontal_scroll_bar_style());
vsb.set_style(style_.get_vertical_scroll_bar_style());
// do this just so that everything gets redrawn right
set_size(rect.width(), rect.height());
}
void show (
);
void hide (
);
void enable (
);
void disable (
);
void set_z_order (
long order
);
virtual void set_size (
unsigned long width,
unsigned long height
);
unsigned long horizontal_mouse_wheel_scroll_increment (
) const;
unsigned long vertical_mouse_wheel_scroll_increment (
) const;
void set_horizontal_mouse_wheel_scroll_increment (
unsigned long inc
);
void set_vertical_mouse_wheel_scroll_increment (
unsigned long inc
);
unsigned long horizontal_scroll_increment (
) const;
unsigned long vertical_scroll_increment (
) const;
void set_horizontal_scroll_increment (
unsigned long inc
);
void set_vertical_scroll_increment (
unsigned long inc
);
long horizontal_scroll_pos (
) const;
long vertical_scroll_pos (
) const;
void set_horizontal_scroll_pos (
long pos
);
void set_vertical_scroll_pos (
long pos
);
virtual void set_pos (
long x,
long y
);
bool mouse_drag_enabled (
) const;
void enable_mouse_drag (
);
void disable_mouse_drag (
);
protected:
virtual void on_view_changed () {}
const rectangle& display_rect (
) const;
void set_total_rect_size (
unsigned long width,
unsigned long height
);
const rectangle& total_rect (
) const;
void scroll_to_rect (
const rectangle& r_
);
void on_wheel_down (
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 on_wheel_up (
unsigned long state
);
void draw (
const canvas& c
) const;
private:
bool need_h_scroll (
) const;
bool need_v_scroll (
) const;
void on_h_scroll (
);
void on_v_scroll (
);
rectangle total_rect_;
rectangle display_rect_;
scroll_bar hsb;
scroll_bar vsb;
unsigned long hscroll_bar_inc;
unsigned long vscroll_bar_inc;
unsigned long h_wheel_scroll_bar_inc;
unsigned long v_wheel_scroll_bar_inc;
bool mouse_drag_enabled_;
bool user_is_dragging_mouse;
point drag_origin;
scoped_ptr<scrollable_region_style> style;
};
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// class popup_menu_region
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
class popup_menu_region : public drawable
{
/*!
CONVENTION
popup_menu_visible() == popup_menu_shown
!*/
public:
popup_menu_region(
drawable_window& w
);
virtual ~popup_menu_region(
);
void set_size (
unsigned long width,
unsigned long height
);
void set_rect (
const rectangle& new_rect
);
popup_menu& menu (
);
void hide (
);
void disable (
);
bool popup_menu_visible (
) const { auto_mutex M(m); return popup_menu_shown; }
protected:
void on_keydown (
unsigned long key,
bool is_printable,
unsigned long state
);
void on_focus_lost (
);
void on_focus_gained (
);
void on_window_moved(
);
void on_mouse_down (
unsigned long btn,
unsigned long state,
long x,
long y,
bool is_double_click
);
void on_menu_becomes_hidden (
);
void draw (
const canvas&
) const;
private:
popup_menu menu_;
bool popup_menu_shown;
// restricted functions
popup_menu_region(popup_menu_region&); // copy constructor
popup_menu_region& operator=(popup_menu_region&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#ifdef NO_MAKEFILE
#include "base_widgets.cpp"
#endif
#endif // DLIB_BASE_WIDGETs_