diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-03-09 13:19:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-03-09 13:20:02 +0000 |
commit | 58daab21cd043e1dc37024a7f99b396788372918 (patch) | |
tree | 96771e43bb69f7c1c2b0b4f7374cb74d7866d0cb /ml/dlib/dlib/gui_widgets/base_widgets.cpp | |
parent | Releasing debian version 1.43.2-1. (diff) | |
download | netdata-58daab21cd043e1dc37024a7f99b396788372918.tar.xz netdata-58daab21cd043e1dc37024a7f99b396788372918.zip |
Merging upstream version 1.44.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ml/dlib/dlib/gui_widgets/base_widgets.cpp')
-rw-r--r-- | ml/dlib/dlib/gui_widgets/base_widgets.cpp | 3343 |
1 files changed, 3343 insertions, 0 deletions
diff --git a/ml/dlib/dlib/gui_widgets/base_widgets.cpp b/ml/dlib/dlib/gui_widgets/base_widgets.cpp new file mode 100644 index 000000000..2f2eb8e9c --- /dev/null +++ b/ml/dlib/dlib/gui_widgets/base_widgets.cpp @@ -0,0 +1,3343 @@ +// 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_CPP_ +#define DLIB_BASE_WIDGETs_CPP_ + +#include <iostream> +#include <memory> + +#include "base_widgets.h" +#include "../assert.h" + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + // button object methods +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + void button:: + set_size ( + unsigned long width, + unsigned long height + ) + { + auto_mutex M(m); + rectangle min_rect = style->get_min_size(name_,*mfont); + // only change the size if it isn't going to be too small to fit the name + if (height >= min_rect.height() && + width >= min_rect.width()) + { + rectangle old(rect); + rect = resize_rect(rect,width,height); + parent.invalidate_rectangle(style->get_invalidation_rect(rect+old)); + btn_tooltip.set_size(width,height); + } + } + +// ---------------------------------------------------------------------------------------- + + void button:: + show ( + ) + { + button_action::show(); + btn_tooltip.show(); + } + +// ---------------------------------------------------------------------------------------- + + void button:: + hide ( + ) + { + button_action::hide(); + btn_tooltip.hide(); + } + +// ---------------------------------------------------------------------------------------- + + void button:: + enable ( + ) + { + button_action::enable(); + btn_tooltip.enable(); + } + +// ---------------------------------------------------------------------------------------- + + void button:: + disable ( + ) + { + button_action::disable(); + btn_tooltip.disable(); + } + +// ---------------------------------------------------------------------------------------- + + void button:: + set_tooltip_text ( + const std::string& text + ) + { + btn_tooltip.set_text(text); + } + +// ---------------------------------------------------------------------------------------- + + void button:: + set_tooltip_text ( + const std::wstring& text + ) + { + btn_tooltip.set_text(text); + } + +// ---------------------------------------------------------------------------------------- + + void button:: + set_tooltip_text ( + const ustring& text + ) + { + btn_tooltip.set_text(text); + } + +// ---------------------------------------------------------------------------------------- + + const std::string button:: + tooltip_text ( + ) const + { + return btn_tooltip.text(); + } + + const std::wstring button:: + tooltip_wtext ( + ) const + { + return btn_tooltip.wtext(); + } + + const dlib::ustring button:: + tooltip_utext ( + ) const + { + return btn_tooltip.utext(); + } + +// ---------------------------------------------------------------------------------------- + + void button:: + set_main_font ( + const std::shared_ptr<font>& f + ) + { + auto_mutex M(m); + mfont = f; + set_name(name_); + } + +// ---------------------------------------------------------------------------------------- + + void button:: + set_pos ( + long x, + long y + ) + { + auto_mutex M(m); + button_action::set_pos(x,y); + btn_tooltip.set_pos(x,y); + } + +// ---------------------------------------------------------------------------------------- + + void button:: + set_name ( + const std::string& name + ) + { + set_name(convert_mbstring_to_wstring(name)); + } + + void button:: + set_name ( + const std::wstring& name + ) + { + set_name(convert_wstring_to_utf32(name)); + } + + void button:: + set_name ( + const ustring& name + ) + { + auto_mutex M(m); + name_ = name; + // do this to get rid of any reference counting that may be present in + // the std::string implementation. + name_[0] = name_[0]; + + rectangle old(rect); + rect = move_rect(style->get_min_size(name,*mfont),rect.left(),rect.top()); + btn_tooltip.set_size(rect.width(),rect.height()); + + parent.invalidate_rectangle(style->get_invalidation_rect(rect+old)); + } + +// ---------------------------------------------------------------------------------------- + + const std::string button:: + name ( + ) const + { + auto_mutex M(m); + std::string temp = convert_wstring_to_mbstring(wname()); + // do this to get rid of any reference counting that may be present in + // the std::string implementation. + char c = temp[0]; + temp[0] = c; + return temp; + } + + const std::wstring button:: + wname ( + ) const + { + auto_mutex M(m); + std::wstring temp = convert_utf32_to_wstring(uname()); + // do this to get rid of any reference counting that may be present in + // the std::wstring implementation. + wchar_t w = temp[0]; + temp[0] = w; + return temp; + } + + const dlib::ustring button:: + uname ( + ) const + { + auto_mutex M(m); + dlib::ustring temp = name_; + // do this to get rid of any reference counting that may be present in + // the dlib::ustring implementation. + temp[0] = name_[0]; + return temp; + } + +// ---------------------------------------------------------------------------------------- + + void button:: + on_button_up ( + bool mouse_over + ) + { + if (mouse_over) + { + // this is a valid button click + if (event_handler.is_set()) + event_handler(); + if (event_handler_self.is_set()) + event_handler_self(*this); + } + if (button_up_handler.is_set()) + button_up_handler(mouse_over); + if (button_up_handler_self.is_set()) + button_up_handler_self(mouse_over,*this); + } + +// ---------------------------------------------------------------------------------------- + + void button:: + on_button_down ( + ) + { + if (button_down_handler.is_set()) + button_down_handler(); + if (button_down_handler_self.is_set()) + button_down_handler_self(*this); + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + // draggable object methods +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + draggable::~draggable() {} + +// ---------------------------------------------------------------------------------------- + + void draggable:: + on_mouse_move ( + unsigned long state, + long x, + long y + ) + { + if (drag && (state & base_window::LEFT) && enabled && !hidden) + { + // the user is trying to drag this object. we should calculate the new + // x and y positions for the upper left corner of this object's rectangle + + long new_x = x - this->x; + long new_y = y - this->y; + + // make sure these points are inside the draggable area. + if (new_x < area.left()) + new_x = area.left(); + if (new_x + static_cast<long>(rect.width()) - 1 > area.right()) + new_x = area.right() - rect.width() + 1; + + if (new_y + static_cast<long>(rect.height()) - 1 > area.bottom()) + new_y = area.bottom() - rect.height() + 1; + if (new_y < area.top()) + new_y = area.top(); + + // now make the new rectangle for this object + rectangle new_rect( + new_x, + new_y, + new_x + rect.width() - 1, + new_y + rect.height() - 1 + ); + + // only do anything if this is a new rectangle and it is inside area + if (new_rect != rect && area.intersect(new_rect) == new_rect) + { + parent.invalidate_rectangle(new_rect + rect); + rect = new_rect; + + // call the on_drag() event handler + on_drag(); + } + } + else + { + drag = false; + on_drag_stop(); + } + } + +// ---------------------------------------------------------------------------------------- + + void draggable:: + on_mouse_up ( + unsigned long , + unsigned long state, + long , + long + ) + { + if (drag && (state & base_window::LEFT) == 0) + { + drag = false; + on_drag_stop(); + } + } + +// ---------------------------------------------------------------------------------------- + + void draggable:: + on_mouse_down ( + unsigned long btn, + unsigned long , + long x, + long y, + bool + ) + { + if (enabled && !hidden && rect.contains(x,y) && btn == base_window::LEFT) + { + drag = true; + this->x = x - rect.left(); + this->y = y - rect.top(); + } + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + // mouse_over_event object methods +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + mouse_over_event::~mouse_over_event() {} + +// ---------------------------------------------------------------------------------------- + + void mouse_over_event:: + on_mouse_leave ( + ) + { + if (is_mouse_over_) + { + is_mouse_over_ = false; + on_mouse_not_over(); + } + } + +// ---------------------------------------------------------------------------------------- + + void mouse_over_event:: + on_mouse_move ( + unsigned long , + long x, + long y + ) + { + if (rect.contains(x,y) == false) + { + if (is_mouse_over_) + { + is_mouse_over_ = false; + on_mouse_not_over(); + } + } + else if (is_mouse_over_ == false) + { + is_mouse_over_ = true; + if (enabled && !hidden) + on_mouse_over(); + } + } + +// ---------------------------------------------------------------------------------------- + + bool mouse_over_event:: + is_mouse_over ( + ) const + { + // check if the mouse is still really over this button + if (is_mouse_over_ && rect.contains(lastx,lasty) == false) + { + // trigger a user event to call on_mouse_not_over() and repaint this object. + // we must do this in another event because someone might call is_mouse_over() + // from draw() and you don't want this function to end up calling + // parent.invalidate_rectangle(). It would lead to draw() being called over + // and over. + parent.trigger_user_event((void*)this,drawable::next_free_user_event_number()); + return false; + } + + return is_mouse_over_; + } + +// ---------------------------------------------------------------------------------------- + + void mouse_over_event:: + on_user_event ( + int num + ) + { + if (is_mouse_over_ && num == drawable::next_free_user_event_number()) + { + is_mouse_over_ = false; + on_mouse_not_over(); + } + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + // button_action object methods +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + button_action::~button_action() {} + +// ---------------------------------------------------------------------------------------- + + void button_action:: + on_mouse_down ( + unsigned long btn, + unsigned long , + long x, + long y, + bool + ) + { + if (enabled && !hidden && btn == base_window::LEFT && rect.contains(x,y)) + { + is_depressed_ = true; + seen_click = true; + parent.invalidate_rectangle(rect); + on_button_down(); + } + } + +// ---------------------------------------------------------------------------------------- + + void button_action:: + on_mouse_not_over ( + ) + { + if (is_depressed_) + { + is_depressed_ = false; + parent.invalidate_rectangle(rect); + on_button_up(false); + } + } + +// ---------------------------------------------------------------------------------------- + + void button_action:: + on_mouse_move ( + unsigned long state, + long x, + long y + ) + { + // forward event to the parent class so it can do it's thing as well as us + mouse_over_event::on_mouse_move(state,x,y); + + if (enabled == false || hidden == true) + return; + + + if ((state & base_window::LEFT) == 0) + { + seen_click = false; + if (is_depressed_) + { + is_depressed_ = false; + parent.invalidate_rectangle(rect); + on_button_up(false); + } + + // the left button isn't down so we don't care about anything else + return; + } + + if (rect.contains(x,y) == false) + { + if (is_depressed_) + { + is_depressed_ = false; + parent.invalidate_rectangle(rect); + on_button_up(false); + } + } + else if (is_depressed_ == false && seen_click) + { + is_depressed_ = true; + parent.invalidate_rectangle(rect); + on_button_down(); + } + } + +// ---------------------------------------------------------------------------------------- + + void button_action:: + on_mouse_up ( + unsigned long btn, + unsigned long, + long x, + long y + ) + { + if (enabled && !hidden && btn == base_window::LEFT) + { + if (is_depressed_) + { + is_depressed_ = false; + parent.invalidate_rectangle(rect); + + if (rect.contains(x,y)) + { + on_button_up(true); + } + else + { + on_button_up(false); + } + } + else if (seen_click && rect.contains(x,y)) + { + // this case here covers the unlikly event that you click on a button, + // move the mouse off the button and then move it back very quickly and + // release the mouse button. It is possible that this mouse up event + // will occurr before any mouse move event so you might not have set + // that the button is depressed yet. + + // So we should say that this triggers an on_button_down() event and + // then an on_button_up(true) event. + + parent.invalidate_rectangle(rect); + + on_button_down(); + on_button_up(true); + } + + seen_click = false; + } + } + +// ---------------------------------------------------------------------------------------- + + bool button_action:: + is_depressed ( + ) const + { + // check if the mouse is still really over this button + if (enabled && !hidden && is_depressed_ && rect.contains(lastx,lasty) == false) + { + // trigger a user event to call on_button_up() and repaint this object. + // we must do this in another event because someone might call is_depressed() + // from draw() and you don't want this function to end up calling + // parent.invalidate_rectangle(). It would lead to draw() being called over + // and over. + parent.trigger_user_event((void*)this,mouse_over_event::next_free_user_event_number()); + return false; + } + + return is_depressed_; + } + +// ---------------------------------------------------------------------------------------- + + void button_action:: + on_user_event ( + int num + ) + { + // forward event to the parent class so it can do it's thing as well as us + mouse_over_event::on_user_event(num); + + if (is_depressed_ && num == mouse_over_event::next_free_user_event_number()) + { + is_depressed_ = false; + parent.invalidate_rectangle(rect); + on_button_up(false); + } + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + // scroll_bar object methods +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + scroll_bar:: + scroll_bar( + drawable_window& w, + bar_orientation orientation + ) : + drawable(w), + b1(w), + b2(w), + slider(w,*this,&scroll_bar::on_slider_drag), + ori(orientation), + top_filler(w,*this,&scroll_bar::top_filler_down,&scroll_bar::top_filler_up), + bottom_filler(w,*this,&scroll_bar::bottom_filler_down,&scroll_bar::bottom_filler_up), + pos(0), + max_pos(0), + js(10), + b1_timer(*this,&scroll_bar::b1_down_t), + b2_timer(*this,&scroll_bar::b2_down_t), + top_filler_timer(*this,&scroll_bar::top_filler_down_t), + bottom_filler_timer(*this,&scroll_bar::bottom_filler_down_t) + { + set_style(scroll_bar_style_default()); + + // don't show the slider when there is no place it can move. + slider.hide(); + + set_length(100); + + b1.set_button_down_handler(*this,&scroll_bar::b1_down); + b2.set_button_down_handler(*this,&scroll_bar::b2_down); + + b1.set_button_up_handler(*this,&scroll_bar::b1_up); + b2.set_button_up_handler(*this,&scroll_bar::b2_up); + b1.disable(); + b2.disable(); + enable_events(); + } + +// ---------------------------------------------------------------------------------------- + + scroll_bar:: + ~scroll_bar( + ) + { + disable_events(); + parent.invalidate_rectangle(rect); + // wait for all the timers to be stopped + b1_timer.stop_and_wait(); + b2_timer.stop_and_wait(); + top_filler_timer.stop_and_wait(); + bottom_filler_timer.stop_and_wait(); + } + +// ---------------------------------------------------------------------------------------- + + scroll_bar::bar_orientation scroll_bar:: + orientation ( + ) const + { + auto_mutex M(m); + return ori; + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + set_length ( + unsigned long length + ) + { + auto_mutex M(m); + // make the min length be at least 1 + if (length == 0) + { + length = 1; + } + + + parent.invalidate_rectangle(rect); + + if (ori == HORIZONTAL) + { + rect.set_right(rect.left() + length - 1); + rect.set_bottom(rect.top() + style->get_width() - 1); + + const long btn_size = style->get_button_length(rect.width(), max_pos); + + b1.set_size(btn_size,style->get_width()); + b2.set_size(btn_size,style->get_width()); + + slider.set_size(get_slider_size(),style->get_width()); + } + else + { + rect.set_right(rect.left() + style->get_width() - 1); + rect.set_bottom(rect.top() + length - 1); + + const long btn_size = style->get_button_length(rect.height(), max_pos); + + b1.set_size(style->get_width(),btn_size); + b2.set_size(style->get_width(),btn_size); + + slider.set_size(style->get_width(),get_slider_size()); + } + + // call this to put everything is in the right spot. + set_pos (rect.left(),rect.top()); + + if ((b2.get_rect().top() - b1.get_rect().bottom() - 1 <= 8 && ori == VERTICAL) || + (b2.get_rect().left() - b1.get_rect().right() - 1 <= 8 && ori == HORIZONTAL) || + max_pos == 0) + { + hide_slider(); + } + else if (enabled && !hidden) + { + show_slider(); + } + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + set_pos ( + long x, + long y + ) + { + auto_mutex M(m); + drawable::set_pos(x,y); + + b1.set_pos(rect.left(),rect.top()); + if (ori == HORIZONTAL) + { + // make the b2 button appear at the end of the scroll_bar + b2.set_pos(rect.right()-b2.get_rect().width() + 1,rect.top()); + + if (max_pos != 0) + { + double range = b2.get_rect().left() - b1.get_rect().right() - slider.get_rect().width() - 1; + double slider_pos = pos; + slider_pos /= max_pos; + slider_pos *= range; + slider.set_pos( + static_cast<long>(slider_pos)+rect.left() + b1.get_rect().width(), + rect.top() + ); + + // move the draggable area for the slider to the new location + rectangle area = rect; + area.set_left(area.left() + style->get_width()); + area.set_right(area.right() - style->get_width()); + slider.set_draggable_area(area); + + } + + + } + else + { + // make the b2 button appear at the end of the scroll_bar + b2.set_pos(rect.left(), rect.bottom() - b2.get_rect().height() + 1); + + if (max_pos != 0) + { + double range = b2.get_rect().top() - b1.get_rect().bottom() - slider.get_rect().height() - 1; + double slider_pos = pos; + slider_pos /= max_pos; + slider_pos *= range; + slider.set_pos( + rect.left(), + static_cast<long>(slider_pos) + rect.top() + b1.get_rect().height() + ); + + // move the draggable area for the slider to the new location + rectangle area = rect; + area.set_top(area.top() + style->get_width()); + area.set_bottom(area.bottom() - style->get_width()); + slider.set_draggable_area(area); + } + } + + adjust_fillers(); + } + +// ---------------------------------------------------------------------------------------- + + unsigned long scroll_bar:: + get_slider_size ( + ) const + { + if (ori == HORIZONTAL) + return style->get_slider_length(rect.width(),max_pos); + else + return style->get_slider_length(rect.height(),max_pos); + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + adjust_fillers ( + ) + { + rectangle top(rect), bottom(rect); + + if (ori == HORIZONTAL) + { + if (slider.is_hidden()) + { + top.set_left(b1.get_rect().right()+1); + top.set_right(b2.get_rect().left()-1); + bottom.set_left(1); + bottom.set_right(-1); + } + else + { + top.set_left(b1.get_rect().right()+1); + top.set_right(slider.get_rect().left()-1); + bottom.set_left(slider.get_rect().right()+1); + bottom.set_right(b2.get_rect().left()-1); + } + } + else + { + if (slider.is_hidden()) + { + top.set_top(b1.get_rect().bottom()+1); + top.set_bottom(b2.get_rect().top()-1); + bottom.set_top(1); + bottom.set_bottom(-1); + } + else + { + top.set_top(b1.get_rect().bottom()+1); + top.set_bottom(slider.get_rect().top()-1); + bottom.set_top(slider.get_rect().bottom()+1); + bottom.set_bottom(b2.get_rect().top()-1); + } + } + + top_filler.rect = top; + bottom_filler.rect = bottom; + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + hide_slider ( + ) + { + rectangle top(rect), bottom(rect); + slider.hide(); + top_filler.disable(); + bottom_filler.disable(); + bottom_filler.hide(); + if (ori == HORIZONTAL) + { + top.set_left(b1.get_rect().right()+1); + top.set_right(b2.get_rect().left()-1); + } + else + { + top.set_top(b1.get_rect().bottom()+1); + top.set_bottom(b2.get_rect().top()-1); + } + top_filler.rect = top; + bottom_filler.rect = bottom; + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + show_slider ( + ) + { + if ((b2.get_rect().top() - b1.get_rect().bottom() - 1 <= 8 && ori == VERTICAL) || + (b2.get_rect().left() - b1.get_rect().right() - 1 <= 8 && ori == HORIZONTAL) || + max_pos == 0) + return; + + rectangle top(rect), bottom(rect); + slider.show(); + top_filler.enable(); + bottom_filler.enable(); + bottom_filler.show(); + if (ori == HORIZONTAL) + { + top.set_left(b1.get_rect().right()+1); + top.set_right(slider.get_rect().left()-1); + bottom.set_left(slider.get_rect().right()+1); + bottom.set_right(b2.get_rect().left()-1); + } + else + { + top.set_top(b1.get_rect().bottom()+1); + top.set_bottom(slider.get_rect().top()-1); + bottom.set_top(slider.get_rect().bottom()+1); + bottom.set_bottom(b2.get_rect().top()-1); + } + top_filler.rect = top; + bottom_filler.rect = bottom; + } + +// ---------------------------------------------------------------------------------------- + + long scroll_bar:: + max_slider_pos ( + ) const + { + auto_mutex M(m); + return max_pos; + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + set_max_slider_pos ( + long mpos + ) + { + auto_mutex M(m); + max_pos = mpos; + if (pos > mpos) + pos = mpos; + + if (ori == HORIZONTAL) + set_length(rect.width()); + else + set_length(rect.height()); + + if (mpos != 0 && enabled) + { + b1.enable(); + b2.enable(); + } + else + { + b1.disable(); + b2.disable(); + } + + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + set_slider_pos ( + long pos + ) + { + auto_mutex M(m); + if (pos < 0) + pos = 0; + if (pos > max_pos) + pos = max_pos; + + this->pos = pos; + + // move the slider object to its new position + set_pos(rect.left(),rect.top()); + } + +// ---------------------------------------------------------------------------------------- + + long scroll_bar:: + slider_pos ( + ) const + { + auto_mutex M(m); + return pos; + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + on_slider_drag ( + ) + { + if (ori == HORIZONTAL) + { + double slider_pos = slider.get_rect().left() - b1.get_rect().right() - 1; + double range = b2.get_rect().left() - b1.get_rect().right() - slider.get_rect().width() - 1; + slider_pos /= range; + slider_pos *= max_pos; + pos = static_cast<unsigned long>(slider_pos); + } + else + { + double slider_pos = slider.get_rect().top() - b1.get_rect().bottom() - 1; + double range = b2.get_rect().top() - b1.get_rect().bottom() - slider.get_rect().height() - 1; + slider_pos /= range; + slider_pos *= max_pos; + pos = static_cast<unsigned long>(slider_pos); + } + + adjust_fillers(); + + if (scroll_handler.is_set()) + scroll_handler(); + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + draw ( + const canvas& + ) const + { + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + b1_down ( + ) + { + if (pos != 0) + { + set_slider_pos(pos-1); + if (scroll_handler.is_set()) + scroll_handler(); + + if (b1_timer.delay_time() == 1000) + b1_timer.set_delay_time(500); + else + b1_timer.set_delay_time(50); + b1_timer.start(); + } + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + b1_up ( + bool + ) + { + b1_timer.stop(); + b1_timer.set_delay_time(1000); + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + b2_down ( + ) + { + if (pos != max_pos) + { + set_slider_pos(pos+1); + if (scroll_handler.is_set()) + scroll_handler(); + + if (b2_timer.delay_time() == 1000) + b2_timer.set_delay_time(500); + else + b2_timer.set_delay_time(50); + b2_timer.start(); + } + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + b2_up ( + bool + ) + { + b2_timer.stop(); + b2_timer.set_delay_time(1000); + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + top_filler_down ( + ) + { + // ignore this if the mouse is now outside this object. This could happen + // since the timers are also calling this function. + if (top_filler.rect.contains(lastx,lasty) == false) + { + top_filler_up(false); + return; + } + + if (pos != 0) + { + if (pos < js) + { + // if there is less than jump_size() space left then jump the remaining + // amount. + delayed_set_slider_pos(0); + } + else + { + delayed_set_slider_pos(pos-js); + } + + if (top_filler_timer.delay_time() == 1000) + top_filler_timer.set_delay_time(500); + else + top_filler_timer.set_delay_time(50); + top_filler_timer.start(); + } + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + top_filler_up ( + bool + ) + { + top_filler_timer.stop(); + top_filler_timer.set_delay_time(1000); + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + bottom_filler_down ( + ) + { + // ignore this if the mouse is now outside this object. This could happen + // since the timers are also calling this function. + if (bottom_filler.rect.contains(lastx,lasty) == false) + { + bottom_filler_up(false); + return; + } + + if (pos != max_pos) + { + if (max_pos - pos < js) + { + // if there is less than jump_size() space left then jump the remaining + // amount. + delayed_set_slider_pos(max_pos); + } + else + { + delayed_set_slider_pos(pos+js); + } + + if (bottom_filler_timer.delay_time() == 1000) + bottom_filler_timer.set_delay_time(500); + else + bottom_filler_timer.set_delay_time(50); + bottom_filler_timer.start(); + } + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + bottom_filler_up ( + bool + ) + { + bottom_filler_timer.stop(); + bottom_filler_timer.set_delay_time(1000); + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + set_jump_size ( + long js_ + ) + { + auto_mutex M(m); + if (js_ < 1) + js = 1; + else + js = js_; + } + +// ---------------------------------------------------------------------------------------- + + long scroll_bar:: + jump_size ( + ) const + { + auto_mutex M(m); + return js; + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + on_user_event ( + int i + ) + { + switch (i) + { + case 0: + b1_down(); + break; + case 1: + b2_down(); + break; + case 2: + top_filler_down(); + break; + case 3: + bottom_filler_down(); + break; + case 4: + // if the position we are supposed to switch the slider too isn't + // already set + if (delayed_pos != pos) + { + set_slider_pos(delayed_pos); + if (scroll_handler.is_set()) + scroll_handler(); + } + break; + default: + break; + } + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + delayed_set_slider_pos ( + unsigned long dpos + ) + { + delayed_pos = dpos; + parent.trigger_user_event(this,4); + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + b1_down_t ( + ) + { + parent.trigger_user_event(this,0); + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + b2_down_t ( + ) + { + parent.trigger_user_event(this,1); + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + top_filler_down_t ( + ) + { + parent.trigger_user_event(this,2); + } + +// ---------------------------------------------------------------------------------------- + + void scroll_bar:: + bottom_filler_down_t ( + ) + { + parent.trigger_user_event(this,3); + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// widget_group object methods +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + void widget_group:: + empty ( + ) + { + auto_mutex M(m); + widgets.clear(); + wg_widgets.clear(); + } + +// ---------------------------------------------------------------------------------------- + + void widget_group:: + add ( + drawable& widget, + unsigned long x, + unsigned long y + ) + { + auto_mutex M(m); + drawable* w = &widget; + relpos rp; + rp.x = x; + rp.y = y; + if (widgets.is_in_domain(w)) + { + widgets[w].x = x; + widgets[w].y = y; + } + else + { + widgets.add(w,rp); + } + if (is_hidden()) + widget.hide(); + else + widget.show(); + + if (is_enabled()) + widget.enable(); + else + widget.disable(); + + widget.set_z_order(z_order()); + widget.set_pos(x+rect.left(),y+rect.top()); + } + +// ---------------------------------------------------------------------------------------- + + void widget_group:: + add ( + widget_group& widget, + unsigned long x, + unsigned long y + ) + { + auto_mutex M(m); + drawable& w = widget; + add(w, x, y); + + widget_group* wg = &widget; + wg_widgets.add(wg); + } + +// ---------------------------------------------------------------------------------------- + + bool widget_group:: + is_member ( + const drawable& widget + ) const + { + auto_mutex M(m); + drawable* w = const_cast<drawable*>(&widget); + return widgets.is_in_domain(w); + } + +// ---------------------------------------------------------------------------------------- + + void widget_group:: + remove ( + const drawable& widget + ) + { + auto_mutex M(m); + drawable* w = const_cast<drawable*>(&widget); + if (widgets.is_in_domain(w)) + { + widgets.destroy(w); + + // check if we also have an entry in the wg_widgets set and if + // so then remove that too + widget_group* wg = reinterpret_cast<widget_group*>(w); + if (wg_widgets.is_member(wg)) + { + wg_widgets.destroy(wg); + } + } + } + +// ---------------------------------------------------------------------------------------- + + size_t widget_group:: + size ( + ) const + { + auto_mutex M(m); + return widgets.size(); + } + +// ---------------------------------------------------------------------------------------- + + void widget_group:: + set_pos ( + long x, + long y + ) + { + auto_mutex M(m); + widgets.reset(); + while (widgets.move_next()) + { + const unsigned long rx = widgets.element().value().x; + const unsigned long ry = widgets.element().value().y; + widgets.element().key()->set_pos(x+rx,y+ry); + } + drawable::set_pos(x,y); + } + +// ---------------------------------------------------------------------------------------- + + void widget_group:: + set_z_order ( + long order + ) + { + auto_mutex M(m); + widgets.reset(); + while (widgets.move_next()) + widgets.element().key()->set_z_order(order); + drawable::set_z_order(order); + } + +// ---------------------------------------------------------------------------------------- + + void widget_group:: + show ( + ) + { + auto_mutex M(m); + widgets.reset(); + while (widgets.move_next()) + widgets.element().key()->show(); + drawable::show(); + } + +// ---------------------------------------------------------------------------------------- + + void widget_group:: + hide ( + ) + { + auto_mutex M(m); + widgets.reset(); + while (widgets.move_next()) + widgets.element().key()->hide(); + drawable::hide(); + } + +// ---------------------------------------------------------------------------------------- + + void widget_group:: + enable ( + ) + { + auto_mutex M(m); + widgets.reset(); + while (widgets.move_next()) + widgets.element().key()->enable(); + drawable::enable(); + } + +// ---------------------------------------------------------------------------------------- + + void widget_group:: + disable () + { + auto_mutex M(m); + widgets.reset(); + while (widgets.move_next()) + widgets.element().key()->disable(); + drawable::disable(); + } + +// ---------------------------------------------------------------------------------------- + + void widget_group:: + fit_to_contents ( + ) + { + auto_mutex M(m); + + // call fit_to_contents on all the widget_groups we contain + wg_widgets.reset(); + while (wg_widgets.move_next()) + wg_widgets.element()->fit_to_contents(); + + // now accumulate a rectangle that contains everything in this widget_group + rectangle r; + widgets.reset(); + while (widgets.move_next()) + r = r + widgets.element().key()->get_rect(); + + if (r.is_empty()) + { + // make sure it is still empty after we set it at the correct position + r.set_right(rect.left()-1); + r.set_bottom(rect.top()-1); + } + + r.set_left(rect.left()); + r.set_top(rect.top()); + rect = r; + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// class popup_menu +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + popup_menu:: + popup_menu ( + ) : + base_window(false,true), + pad(2), + item_pad(3), + cur_rect(pad,pad,pad-1,pad-1), + left_width(0), + middle_width(0), + selected_item(0), + submenu_open(false) + { + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu:: + enable_menu_item ( + unsigned long idx + ) + { + DLIB_ASSERT ( idx < size() , + "\tvoid popup_menu::enable_menu_item()" + << "\n\tidx: " << idx + << "\n\tsize(): " << size() + ); + auto_mutex M(wm); + item_enabled[idx] = true; + invalidate_rectangle(cur_rect); + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu:: + disable_menu_item ( + unsigned long idx + ) + { + DLIB_ASSERT ( idx < size() , + "\tvoid popup_menu::enable_menu_item()" + << "\n\tidx: " << idx + << "\n\tsize(): " << size() + ); + auto_mutex M(wm); + item_enabled[idx] = false; + invalidate_rectangle(cur_rect); + } + +// ---------------------------------------------------------------------------------------- + + size_t popup_menu:: + size ( + ) const + { + auto_mutex M(wm); + return items.size(); + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu:: + clear ( + ) + { + auto_mutex M(wm); + hide(); + cur_rect = rectangle(pad,pad,pad-1,pad-1); + win_rect = rectangle(); + left_width = 0; + middle_width = 0; + items.clear(); + item_enabled.clear(); + left_rects.clear(); + middle_rects.clear(); + right_rects.clear(); + line_rects.clear(); + submenus.clear(); + selected_item = 0; + submenu_open = false; + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu:: + show ( + ) + { + auto_mutex M(wm); + selected_item = submenus.size(); + base_window::show(); + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu:: + hide ( + ) + { + auto_mutex M(wm); + // hide ourselves + close_submenu(); + selected_item = submenus.size(); + base_window::hide(); + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu:: + select_first_item ( + ) + { + auto_mutex M(wm); + close_submenu(); + selected_item = items.size(); + for (unsigned long i = 0; i < items.size(); ++i) + { + if ((items[i]->has_click_event() || submenus[i]) && item_enabled[i]) + { + selected_item = i; + break; + } + } + invalidate_rectangle(cur_rect); + } + +// ---------------------------------------------------------------------------------------- + + bool popup_menu:: + forwarded_on_keydown ( + unsigned long key, + bool is_printable, + unsigned long state + ) + { + auto_mutex M(wm); + // do nothing if this popup menu is empty + if (items.size() == 0) + return false; + + + // check if the selected item is a submenu + if (selected_item != submenus.size() && submenus[selected_item] != 0 && submenu_open) + { + // send the key to the submenu and return if that menu used the key + if (submenus[selected_item]->forwarded_on_keydown(key,is_printable,state) == true) + return true; + } + + if (key == KEY_UP) + { + for (unsigned long i = 0; i < items.size(); ++i) + { + selected_item = (selected_item + items.size() - 1)%items.size(); + // only stop looking if this one is enabled and has a click event or is a submenu + if (item_enabled[selected_item] && (items[selected_item]->has_click_event() || submenus[selected_item]) ) + break; + } + invalidate_rectangle(cur_rect); + return true; + } + else if (key == KEY_DOWN) + { + for (unsigned long i = 0; i < items.size(); ++i) + { + selected_item = (selected_item + 1)%items.size(); + // only stop looking if this one is enabled and has a click event or is a submenu + if (item_enabled[selected_item] && (items[selected_item]->has_click_event() || submenus[selected_item])) + break; + } + invalidate_rectangle(cur_rect); + return true; + } + else if (key == KEY_RIGHT && submenu_open == false && display_selected_submenu()) + { + submenus[selected_item]->select_first_item(); + return true; + } + else if (key == KEY_LEFT && selected_item != submenus.size() && + submenus[selected_item] != 0 && submenu_open) + { + close_submenu(); + return true; + } + else if (key == '\n') + { + if (selected_item != submenus.size() && (items[selected_item]->has_click_event() || submenus[selected_item])) + { + const long idx = selected_item; + // only hide this popup window if this isn't a submenu + if (submenus[idx] == 0) + { + hide(); + hide_handlers.reset(); + while (hide_handlers.move_next()) + hide_handlers.element()(); + } + else + { + display_selected_submenu(); + submenus[idx]->select_first_item(); + } + items[idx]->on_click(); + return true; + } + } + else if (is_printable) + { + // check if there is a hotkey for this key + for (unsigned long i = 0; i < items.size(); ++i) + { + if (std::tolower(key) == std::tolower(items[i]->get_hot_key()) && + (items[i]->has_click_event() || submenus[i]) && item_enabled[i] ) + { + // only hide this popup window if this isn't a submenu + if (submenus[i] == 0) + { + hide(); + hide_handlers.reset(); + while (hide_handlers.move_next()) + hide_handlers.element()(); + } + else + { + if (selected_item != items.size()) + invalidate_rectangle(line_rects[selected_item]); + + selected_item = i; + display_selected_submenu(); + invalidate_rectangle(line_rects[i]); + submenus[i]->select_first_item(); + } + items[i]->on_click(); + } + } + + // always say we use a printable key for hotkeys + return true; + } + + return false; + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu:: + on_submenu_hide ( + ) + { + hide(); + hide_handlers.reset(); + while (hide_handlers.move_next()) + hide_handlers.element()(); + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu:: + on_window_resized( + ) + { + invalidate_rectangle(win_rect); + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu:: + on_mouse_up ( + unsigned long btn, + unsigned long, + long x, + long y + ) + { + if (cur_rect.contains(x,y) && btn == LEFT) + { + // figure out which item this was on + for (unsigned long i = 0; i < items.size(); ++i) + { + if (line_rects[i].contains(x,y) && item_enabled[i] && items[i]->has_click_event()) + { + // only hide this popup window if this isn't a submenu + if (submenus[i] == 0) + { + hide(); + hide_handlers.reset(); + while (hide_handlers.move_next()) + hide_handlers.element()(); + } + items[i]->on_click(); + break; + } + } + } + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu:: + on_mouse_move ( + unsigned long , + long x, + long y + ) + { + if (cur_rect.contains(x,y)) + { + // check if the mouse is still in the same rect it was in last time + rectangle last_rect; + if (selected_item != submenus.size()) + { + last_rect = line_rects[selected_item]; + } + + // if the mouse isn't in the same rectangle any more + if (last_rect.contains(x,y) == false) + { + if (selected_item != submenus.size()) + { + invalidate_rectangle(last_rect); + close_submenu(); + selected_item = submenus.size(); + } + + + // figure out if we should redraw any menu items + for (unsigned long i = 0; i < items.size(); ++i) + { + if (items[i]->has_click_event() || submenus[i]) + { + if (line_rects[i].contains(x,y)) + { + selected_item = i; + break; + } + } + } + + // if we found a rectangle that contains the mouse then + // tell it to redraw itself + if (selected_item != submenus.size()) + { + display_selected_submenu(); + invalidate_rectangle(line_rects[selected_item]); + } + } + } + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu:: + close_submenu ( + ) + { + if (selected_item != submenus.size() && submenus[selected_item] && submenu_open) + { + submenus[selected_item]->hide(); + submenu_open = false; + } + } + +// ---------------------------------------------------------------------------------------- + + bool popup_menu:: + display_selected_submenu ( + ) + { + // show the submenu if one exists + if (selected_item != submenus.size() && submenus[selected_item]) + { + long wx, wy; + get_pos(wx,wy); + wx += line_rects[selected_item].right(); + wy += line_rects[selected_item].top(); + submenus[selected_item]->set_pos(wx+1,wy-2); + submenus[selected_item]->show(); + submenu_open = true; + return true; + } + return false; + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu:: + on_mouse_leave ( + ) + { + if (selected_item != submenus.size()) + { + // only unhighlight a menu item if it isn't a submenu item + if (submenus[selected_item] == 0) + { + invalidate_rectangle(line_rects[selected_item]); + selected_item = submenus.size(); + } + } + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu:: + paint ( + const canvas& c + ) + { + c.fill(200,200,200); + draw_rectangle(c, win_rect); + for (unsigned long i = 0; i < items.size(); ++i) + { + bool is_selected = false; + if (selected_item != submenus.size() && i == selected_item && + item_enabled[i]) + is_selected = true; + + items[i]->draw_background(c,line_rects[i], item_enabled[i], is_selected); + items[i]->draw_left(c,left_rects[i], item_enabled[i], is_selected); + items[i]->draw_middle(c,middle_rects[i], item_enabled[i], is_selected); + items[i]->draw_right(c,right_rects[i], item_enabled[i], is_selected); + } + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// class zoomable_region +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + zoomable_region:: + zoomable_region ( + drawable_window& w, + unsigned long events + ) : + drawable(w,MOUSE_CLICK | MOUSE_WHEEL | MOUSE_MOVE | events), + min_scale(0.15), + max_scale(1.0), + zoom_increment_(0.90), + vsb(w, scroll_bar::VERTICAL), + hsb(w, scroll_bar::HORIZONTAL) + { + scale = 1; + mouse_drag_screen = false; + style.reset(new scrollable_region_style_default()); + + hsb.set_scroll_handler(*this,&zoomable_region::on_h_scroll); + vsb.set_scroll_handler(*this,&zoomable_region::on_v_scroll); + } + +// ---------------------------------------------------------------------------------------- + + zoomable_region:: + ~zoomable_region() + { + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + set_pos ( + long x, + long y + ) + { + auto_mutex M(m); + drawable::set_pos(x,y); + const long border_size = style->get_border_size(); + vsb.set_pos(rect.right()-border_size+1-vsb.width(),rect.top()+border_size); + hsb.set_pos(rect.left()+border_size,rect.bottom()-border_size+1-hsb.height()); + + display_rect_ = rectangle(rect.left()+border_size, + rect.top()+border_size, + rect.right()-border_size-vsb.width(), + rect.bottom()-border_size-hsb.height()); + + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + set_zoom_increment ( + double zi + ) + { + DLIB_ASSERT(0.0 < zi && zi < 1.0, + "\tvoid zoomable_region::set_zoom_increment(zi)" + << "\n\t the zoom increment must be between 0 and 1" + << "\n\t zi: " << zi + << "\n\t this: " << this + ); + + auto_mutex M(m); + zoom_increment_ = zi; + } + +// ---------------------------------------------------------------------------------------- + + double zoomable_region:: + zoom_increment ( + ) const + { + auto_mutex M(m); + return zoom_increment_; + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + set_max_zoom_scale ( + double ms + ) + { + DLIB_ASSERT(ms > 0, + "\tvoid zoomable_region::set_max_zoom_scale(ms)" + << "\n\t the max zoom scale must be greater than 0" + << "\n\t ms: " << ms + << "\n\t this: " << this + ); + + auto_mutex M(m); + max_scale = ms; + if (scale > ms) + { + scale = max_scale; + lr_point = gui_to_graph_space(point(display_rect_.right(),display_rect_.bottom())); + redraw_graph(); + } + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + set_min_zoom_scale ( + double ms + ) + { + DLIB_ASSERT(ms > 0, + "\tvoid zoomable_region::set_min_zoom_scale(ms)" + << "\n\t the min zoom scale must be greater than 0" + << "\n\t ms: " << ms + << "\n\t this: " << this + ); + + auto_mutex M(m); + min_scale = ms; + + if (scale < ms) + { + scale = min_scale; + } + + // just call set_size so that everything gets redrawn right + set_size(rect.width(), rect.height()); + } + +// ---------------------------------------------------------------------------------------- + + double zoomable_region:: + min_zoom_scale ( + ) const + { + auto_mutex M(m); + return min_scale; + } + +// ---------------------------------------------------------------------------------------- + + double zoomable_region:: + max_zoom_scale ( + ) const + { + auto_mutex M(m); + return max_scale; + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + set_size ( + unsigned long width, + unsigned long height + ) + { + auto_mutex M(m); + rectangle old(rect); + const long border_size = style->get_border_size(); + rect = resize_rect(rect,width,height); + vsb.set_pos(rect.right()-border_size+1-vsb.width(), rect.top()+border_size); + hsb.set_pos(rect.left()+border_size, rect.bottom()-border_size+1-hsb.height()); + + display_rect_ = rectangle(rect.left()+border_size, + rect.top()+border_size, + rect.right()-border_size-vsb.width(), + rect.bottom()-border_size-hsb.height()); + vsb.set_length(display_rect_.height()); + hsb.set_length(display_rect_.width()); + parent.invalidate_rectangle(rect+old); + + const double old_scale = scale; + const vector<double,2> old_gr_orig(gr_orig); + scale = min_scale; + gr_orig = vector<double,2>(0,0); + lr_point = gui_to_graph_space(point(display_rect_.right(),display_rect_.bottom())); + scale = old_scale; + + // call adjust_origin() so that the scroll bars get their max slider positions + // setup right + const point rect_corner(display_rect_.left(), display_rect_.top()); + adjust_origin(rect_corner, old_gr_orig); + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + show ( + ) + { + auto_mutex M(m); + drawable::show(); + hsb.show(); + vsb.show(); + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + hide ( + ) + { + auto_mutex M(m); + drawable::hide(); + hsb.hide(); + vsb.hide(); + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + enable ( + ) + { + auto_mutex M(m); + drawable::enable(); + hsb.enable(); + vsb.enable(); + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + disable ( + ) + { + auto_mutex M(m); + drawable::disable(); + hsb.disable(); + vsb.disable(); + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + set_z_order ( + long order + ) + { + auto_mutex M(m); + drawable::set_z_order(order); + hsb.set_z_order(order); + vsb.set_z_order(order); + } + +// ---------------------------------------------------------------------------------------- + + point zoomable_region:: + graph_to_gui_space ( + const vector<double,2>& p + ) const + { + const point rect_corner(display_rect_.left(), display_rect_.top()); + return (p - gr_orig)*scale + rect_corner; + } + +// ---------------------------------------------------------------------------------------- + + vector<double,2> zoomable_region:: + gui_to_graph_space ( + const point& p + ) const + { + const point rect_corner(display_rect_.left(), display_rect_.top()); + return (p - rect_corner)/scale + gr_orig; + } + +// ---------------------------------------------------------------------------------------- + + point zoomable_region:: + max_graph_point ( + ) const + { + return lr_point; + } + +// ---------------------------------------------------------------------------------------- + + rectangle zoomable_region:: + display_rect ( + ) const + { + return display_rect_; + } + +// ---------------------------------------------------------------------------------------- + + double zoomable_region:: + zoom_scale ( + ) const + { + return scale; + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + set_zoom_scale ( + double new_scale + ) + { + // if new_scale isn't in the right range then put it back in range before we do the + // rest of this function + if (!(min_scale <= new_scale && new_scale <= max_scale)) + { + if (new_scale > max_scale) + new_scale = max_scale; + else + new_scale = min_scale; + } + + // find the point in the center of the graph area + point center((display_rect_.left()+display_rect_.right())/2, (display_rect_.top()+display_rect_.bottom())/2); + point graph_p(gui_to_graph_space(center)); + scale = new_scale; + adjust_origin(center, graph_p); + redraw_graph(); + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + center_display_at_graph_point ( + const vector<double,2>& p + ) + { + // find the point in the center of the graph area + point center((display_rect_.left()+display_rect_.right())/2, (display_rect_.top()+display_rect_.bottom())/2); + adjust_origin(center, p); + redraw_graph(); + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + on_wheel_down ( + unsigned long + ) + { + // zoom out + if (enabled && !hidden && scale > min_scale && display_rect_.contains(lastx,lasty)) + { + point gui_p(lastx,lasty); + point graph_p(gui_to_graph_space(gui_p)); + const double old_scale = scale; + scale *= zoom_increment_; + if (scale < min_scale) + scale = min_scale; + redraw_graph(); + adjust_origin(gui_p, graph_p); + + if (scale != old_scale) + on_view_changed(); + } + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + on_wheel_up ( + unsigned long + ) + { + // zoom in + if (enabled && !hidden && scale < max_scale && display_rect_.contains(lastx,lasty)) + { + point gui_p(lastx,lasty); + point graph_p(gui_to_graph_space(gui_p)); + const double old_scale = scale; + scale /= zoom_increment_; + if (scale > max_scale) + scale = max_scale; + redraw_graph(); + adjust_origin(gui_p, graph_p); + + if (scale != old_scale) + on_view_changed(); + } + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + on_mouse_move ( + unsigned long state, + long x, + long y + ) + { + if (enabled && !hidden && mouse_drag_screen) + { + adjust_origin(point(x,y), drag_screen_point); + redraw_graph(); + on_view_changed(); + } + + // check if the mouse isn't being dragged anymore + if ((state & base_window::LEFT) == 0) + { + mouse_drag_screen = false; + } + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + on_mouse_up ( + unsigned long , + unsigned long , + long , + long + ) + { + mouse_drag_screen = false; + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + on_mouse_down ( + unsigned long btn, + unsigned long , + long x, + long y, + bool + ) + { + if (enabled && !hidden && display_rect_.contains(x,y) && btn == base_window::LEFT) + { + mouse_drag_screen = true; + drag_screen_point = gui_to_graph_space(point(x,y)); + } + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + draw ( + const canvas& c + ) const + { + style->draw_scrollable_region_border(c, rect, enabled); + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + on_h_scroll ( + ) + { + gr_orig.x() = hsb.slider_pos(); + redraw_graph(); + + on_view_changed(); + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + on_v_scroll ( + ) + { + gr_orig.y() = vsb.slider_pos(); + redraw_graph(); + + on_view_changed(); + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + redraw_graph ( + ) + { + parent.invalidate_rectangle(display_rect_); + } + +// ---------------------------------------------------------------------------------------- + + void zoomable_region:: + adjust_origin ( + const point& gui_p, + const vector<double,2>& graph_p + ) + { + const point rect_corner(display_rect_.left(), display_rect_.top()); + const dlib::vector<double,2> v(gui_p - rect_corner); + gr_orig = graph_p - v/scale; + + + // make sure the origin isn't outside the point (0,0) + if (gr_orig.x() < 0) + gr_orig.x() = 0; + if (gr_orig.y() < 0) + gr_orig.y() = 0; + + // make sure the lower right corner of the display_rect_ doesn't map to a point beyond lr_point + point lr_rect_corner(display_rect_.right(), display_rect_.bottom()); + point p = graph_to_gui_space(lr_point); + vector<double,2> lr_rect_corner_graph_space(gui_to_graph_space(lr_rect_corner)); + vector<double,2> delta(lr_point - lr_rect_corner_graph_space); + if (lr_rect_corner.x() > p.x()) + { + gr_orig.x() += delta.x(); + } + + if (lr_rect_corner.y() > p.y()) + { + gr_orig.y() += delta.y(); + } + + + const vector<double,2> ul_rect_corner_graph_space(gui_to_graph_space(rect_corner)); + lr_rect_corner_graph_space = gui_to_graph_space(lr_rect_corner); + // now adjust the scroll bars + + hsb.set_max_slider_pos((unsigned long)std::max(lr_point.x()-(lr_rect_corner_graph_space.x()-ul_rect_corner_graph_space.x()),0.0)); + vsb.set_max_slider_pos((unsigned long)std::max(lr_point.y()-(lr_rect_corner_graph_space.y()-ul_rect_corner_graph_space.y()),0.0)); + // adjust slider position now. + hsb.set_slider_pos(static_cast<long>(ul_rect_corner_graph_space.x())); + vsb.set_slider_pos(static_cast<long>(ul_rect_corner_graph_space.y())); + + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// class scrollable_region +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + scrollable_region:: + scrollable_region ( + drawable_window& w, + unsigned long events + ) : + drawable(w, MOUSE_WHEEL|events|MOUSE_CLICK|MOUSE_MOVE), + hsb(w,scroll_bar::HORIZONTAL), + vsb(w,scroll_bar::VERTICAL), + 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) + { + style.reset(new scrollable_region_style_default()); + + hsb.set_scroll_handler(*this,&scrollable_region::on_h_scroll); + vsb.set_scroll_handler(*this,&scrollable_region::on_v_scroll); + } + +// ---------------------------------------------------------------------------------------- + + scrollable_region:: + ~scrollable_region ( + ) + { + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + show ( + ) + { + auto_mutex M(m); + drawable::show(); + if (need_h_scroll()) + hsb.show(); + if (need_v_scroll()) + vsb.show(); + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + hide ( + ) + { + auto_mutex M(m); + drawable::hide(); + hsb.hide(); + vsb.hide(); + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + enable ( + ) + { + auto_mutex M(m); + drawable::enable(); + hsb.enable(); + vsb.enable(); + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + disable ( + ) + { + auto_mutex M(m); + drawable::disable(); + hsb.disable(); + vsb.disable(); + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + set_z_order ( + long order + ) + { + auto_mutex M(m); + drawable::set_z_order(order); + hsb.set_z_order(order); + vsb.set_z_order(order); + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + set_size ( + unsigned long width, + unsigned long height + ) + { + auto_mutex M(m); + rectangle old(rect); + rect = resize_rect(rect,width,height); + vsb.set_pos(rect.right()-style->get_border_size()-vsb.width()+1, rect.top()+style->get_border_size()); + hsb.set_pos(rect.left()+style->get_border_size(), rect.bottom()-style->get_border_size()-hsb.height()+1); + + // adjust the display_rect_ + if (need_h_scroll() && need_v_scroll()) + { + // both scroll bars aren't hidden + if (!hidden) + { + vsb.show(); + hsb.show(); + } + display_rect_ = rectangle( rect.left()+style->get_border_size(), + rect.top()+style->get_border_size(), + rect.right()-style->get_border_size()-vsb.width(), + rect.bottom()-style->get_border_size()-hsb.height()); + + // figure out how many scroll bar positions there should be + unsigned long hdelta = total_rect_.width()-display_rect_.width(); + unsigned long vdelta = total_rect_.height()-display_rect_.height(); + hdelta = (hdelta+hscroll_bar_inc-1)/hscroll_bar_inc; + vdelta = (vdelta+vscroll_bar_inc-1)/vscroll_bar_inc; + + hsb.set_max_slider_pos(hdelta); + vsb.set_max_slider_pos(vdelta); + + vsb.set_jump_size((display_rect_.height()+vscroll_bar_inc-1)/vscroll_bar_inc/2+1); + hsb.set_jump_size((display_rect_.width()+hscroll_bar_inc-1)/hscroll_bar_inc/2+1); + } + else if (need_h_scroll()) + { + // only hsb is hidden + if (!hidden) + { + hsb.show(); + vsb.hide(); + } + display_rect_ = rectangle( rect.left()+style->get_border_size(), + rect.top()+style->get_border_size(), + rect.right()-style->get_border_size(), + rect.bottom()-style->get_border_size()-hsb.height()); + + // figure out how many scroll bar positions there should be + unsigned long hdelta = total_rect_.width()-display_rect_.width(); + hdelta = (hdelta+hscroll_bar_inc-1)/hscroll_bar_inc; + + hsb.set_max_slider_pos(hdelta); + vsb.set_max_slider_pos(0); + + hsb.set_jump_size((display_rect_.width()+hscroll_bar_inc-1)/hscroll_bar_inc/2+1); + } + else if (need_v_scroll()) + { + // only vsb is hidden + if (!hidden) + { + hsb.hide(); + vsb.show(); + } + display_rect_ = rectangle( rect.left()+style->get_border_size(), + rect.top()+style->get_border_size(), + rect.right()-style->get_border_size()-vsb.width(), + rect.bottom()-style->get_border_size()); + + unsigned long vdelta = total_rect_.height()-display_rect_.height(); + vdelta = (vdelta+vscroll_bar_inc-1)/vscroll_bar_inc; + + hsb.set_max_slider_pos(0); + vsb.set_max_slider_pos(vdelta); + + vsb.set_jump_size((display_rect_.height()+vscroll_bar_inc-1)/vscroll_bar_inc/2+1); + } + else + { + // both are hidden + if (!hidden) + { + hsb.hide(); + vsb.hide(); + } + display_rect_ = rectangle( rect.left()+style->get_border_size(), + rect.top()+style->get_border_size(), + rect.right()-style->get_border_size(), + rect.bottom()-style->get_border_size()); + + hsb.set_max_slider_pos(0); + vsb.set_max_slider_pos(0); + } + + vsb.set_length(display_rect_.height()); + hsb.set_length(display_rect_.width()); + + // adjust the total_rect_ position by trigging the scroll events + on_h_scroll(); + on_v_scroll(); + + parent.invalidate_rectangle(rect+old); + } + +// ---------------------------------------------------------------------------------------- + + unsigned long scrollable_region:: + horizontal_mouse_wheel_scroll_increment ( + ) const + { + auto_mutex M(m); + return h_wheel_scroll_bar_inc; + } + +// ---------------------------------------------------------------------------------------- + + unsigned long scrollable_region:: + vertical_mouse_wheel_scroll_increment ( + ) const + { + auto_mutex M(m); + return v_wheel_scroll_bar_inc; + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + set_horizontal_mouse_wheel_scroll_increment ( + unsigned long inc + ) + { + auto_mutex M(m); + h_wheel_scroll_bar_inc = inc; + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + set_vertical_mouse_wheel_scroll_increment ( + unsigned long inc + ) + { + auto_mutex M(m); + v_wheel_scroll_bar_inc = inc; + } + +// ---------------------------------------------------------------------------------------- + + unsigned long scrollable_region:: + horizontal_scroll_increment ( + ) const + { + auto_mutex M(m); + return hscroll_bar_inc; + } + +// ---------------------------------------------------------------------------------------- + + unsigned long scrollable_region:: + vertical_scroll_increment ( + ) const + { + auto_mutex M(m); + return vscroll_bar_inc; + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + set_horizontal_scroll_increment ( + unsigned long inc + ) + { + auto_mutex M(m); + hscroll_bar_inc = inc; + // call set_size to reset the scroll bars + set_size(rect.width(),rect.height()); + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + set_vertical_scroll_increment ( + unsigned long inc + ) + { + auto_mutex M(m); + vscroll_bar_inc = inc; + // call set_size to reset the scroll bars + set_size(rect.width(),rect.height()); + } + +// ---------------------------------------------------------------------------------------- + + long scrollable_region:: + horizontal_scroll_pos ( + ) const + { + auto_mutex M(m); + return hsb.slider_pos(); + } + +// ---------------------------------------------------------------------------------------- + + long scrollable_region:: + vertical_scroll_pos ( + ) const + { + auto_mutex M(m); + return vsb.slider_pos(); + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + set_horizontal_scroll_pos ( + long pos + ) + { + auto_mutex M(m); + + hsb.set_slider_pos(pos); + on_h_scroll(); + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + set_vertical_scroll_pos ( + long pos + ) + { + auto_mutex M(m); + + vsb.set_slider_pos(pos); + on_v_scroll(); + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + set_pos ( + long x, + long y + ) + { + auto_mutex M(m); + drawable::set_pos(x,y); + vsb.set_pos(rect.right()-style->get_border_size()-vsb.width()+1, rect.top()+style->get_border_size()); + hsb.set_pos(rect.left()+style->get_border_size(), rect.bottom()-style->get_border_size()-hsb.height()+1); + + const long delta_x = total_rect_.left() - display_rect_.left(); + const long delta_y = total_rect_.top() - display_rect_.top(); + + display_rect_ = move_rect(display_rect_, rect.left()+style->get_border_size(), rect.top()+style->get_border_size()); + + total_rect_ = move_rect(total_rect_, display_rect_.left()+delta_x, display_rect_.top()+delta_y); + } + +// ---------------------------------------------------------------------------------------- + + bool scrollable_region:: + mouse_drag_enabled ( + ) const + { + auto_mutex M(m); + return mouse_drag_enabled_; + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + enable_mouse_drag ( + ) + { + auto_mutex M(m); + mouse_drag_enabled_ = true; + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + disable_mouse_drag ( + ) + { + auto_mutex M(m); + mouse_drag_enabled_ = false; + } + +// ---------------------------------------------------------------------------------------- + + const rectangle& scrollable_region:: + display_rect ( + ) const + { + return display_rect_; + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + set_total_rect_size ( + unsigned long width, + unsigned long height + ) + { + DLIB_ASSERT((width > 0 && height > 0) || (width == 0 && height == 0), + "\tvoid scrollable_region::set_total_rect_size(width,height)" + << "\n\twidth and height must be > 0 or both == 0" + << "\n\twidth: " << width + << "\n\theight: " << height + << "\n\tthis: " << this + ); + + total_rect_ = move_rect(rectangle(width,height), + display_rect_.left()-static_cast<long>(hsb.slider_pos()), + display_rect_.top()-static_cast<long>(vsb.slider_pos())); + + // call this just to reconfigure the scroll bars + set_size(rect.width(),rect.height()); + } + +// ---------------------------------------------------------------------------------------- + + const rectangle& scrollable_region:: + total_rect ( + ) const + { + return total_rect_; + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + scroll_to_rect ( + const rectangle& r_ + ) + { + const rectangle r(total_rect_.intersect(r_)); + const rectangle old(total_rect_); + // adjust the horizontal scroll bar so that r fits as best as possible + if (r.left() < display_rect_.left()) + { + long distance = (r.left()-total_rect_.left())/hscroll_bar_inc; + hsb.set_slider_pos(distance); + } + else if (r.right() > display_rect_.right()) + { + long distance = (r.right()-total_rect_.left()-display_rect_.width()+hscroll_bar_inc)/hscroll_bar_inc; + hsb.set_slider_pos(distance); + } + + // adjust the vertical scroll bar so that r fits as best as possible + if (r.top() < display_rect_.top()) + { + long distance = (r.top()-total_rect_.top())/vscroll_bar_inc; + vsb.set_slider_pos(distance); + } + else if (r.bottom() > display_rect_.bottom()) + { + long distance = (r.bottom()-total_rect_.top()-display_rect_.height()+vscroll_bar_inc)/vscroll_bar_inc; + vsb.set_slider_pos(distance); + } + + + // adjust total_rect_ so that it matches where the scroll bars are now + total_rect_ = move_rect(total_rect_, + display_rect_.left()-hscroll_bar_inc*hsb.slider_pos(), + display_rect_.top()-vscroll_bar_inc*vsb.slider_pos()); + + // only redraw if we actually changed something + if (total_rect_ != old) + { + parent.invalidate_rectangle(display_rect_); + } + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + on_wheel_down ( + unsigned long + ) + { + if (rect.contains(lastx,lasty) && enabled && !hidden) + { + if (need_v_scroll()) + { + long pos = vsb.slider_pos(); + vsb.set_slider_pos(pos+(long)v_wheel_scroll_bar_inc); + on_v_scroll(); + } + else if (need_h_scroll()) + { + long pos = hsb.slider_pos(); + hsb.set_slider_pos(pos+(long)h_wheel_scroll_bar_inc); + on_h_scroll(); + } + } + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + on_mouse_move ( + unsigned long state, + long x, + long y + ) + { + if (enabled && !hidden && user_is_dragging_mouse && state==base_window::LEFT) + { + point current_delta = point(x,y) - point(total_rect().left(), total_rect().top()); + rectangle new_rect(translate_rect(display_rect(), drag_origin - current_delta)); + new_rect = centered_rect(new_rect, new_rect.width()-hscroll_bar_inc, new_rect.height()-vscroll_bar_inc); + scroll_to_rect(new_rect); + on_view_changed(); + } + else + { + user_is_dragging_mouse = false; + } + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + on_mouse_down ( + unsigned long btn, + unsigned long , + long x, + long y, + bool + ) + { + if (mouse_drag_enabled_ && enabled && !hidden && display_rect().contains(x,y) && (btn==base_window::LEFT)) + { + drag_origin = point(x,y) - point(total_rect().left(), total_rect().top()); + user_is_dragging_mouse = true; + } + else + { + user_is_dragging_mouse = false; + } + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + on_mouse_up ( + unsigned long , + unsigned long , + long , + long + ) + { + user_is_dragging_mouse = false; + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + on_wheel_up ( + unsigned long + ) + { + if (rect.contains(lastx,lasty) && enabled && !hidden) + { + if (need_v_scroll()) + { + long pos = vsb.slider_pos(); + vsb.set_slider_pos(pos-(long)v_wheel_scroll_bar_inc); + on_v_scroll(); + } + else if (need_h_scroll()) + { + long pos = hsb.slider_pos(); + hsb.set_slider_pos(pos-(long)h_wheel_scroll_bar_inc); + on_h_scroll(); + } + } + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + draw ( + const canvas& c + ) const + { + style->draw_scrollable_region_border(c, rect, enabled); + } + +// ---------------------------------------------------------------------------------------- + + bool scrollable_region:: + need_h_scroll ( + ) const + { + if (total_rect_.width() > rect.width()-style->get_border_size()*2) + { + return true; + } + else + { + // check if we would need a vertical scroll bar and if adding one would make us need + // a horizontal one + if (total_rect_.height() > rect.height()-style->get_border_size()*2 && + total_rect_.width() > rect.width()-style->get_border_size()*2-vsb.width()) + return true; + else + return false; + } + } + +// ---------------------------------------------------------------------------------------- + + bool scrollable_region:: + need_v_scroll ( + ) const + { + if (total_rect_.height() > rect.height()-style->get_border_size()*2) + { + return true; + } + else + { + // check if we would need a horizontal scroll bar and if adding one would make us need + // a vertical_scroll_pos one + if (total_rect_.width() > rect.width()-style->get_border_size()*2 && + total_rect_.height() > rect.height()-style->get_border_size()*2-hsb.height()) + return true; + else + return false; + } + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + on_h_scroll ( + ) + { + total_rect_ = move_rect(total_rect_, display_rect_.left()-hscroll_bar_inc*hsb.slider_pos(), total_rect_.top()); + parent.invalidate_rectangle(display_rect_); + if (events_are_enabled()) + on_view_changed(); + } + +// ---------------------------------------------------------------------------------------- + + void scrollable_region:: + on_v_scroll ( + ) + { + total_rect_ = move_rect(total_rect_, total_rect_.left(), display_rect_.top()-vscroll_bar_inc*vsb.slider_pos()); + parent.invalidate_rectangle(display_rect_); + if (events_are_enabled()) + on_view_changed(); + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// class popup_menu_region +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + popup_menu_region:: + popup_menu_region( + drawable_window& w + ) : + drawable(w,MOUSE_CLICK | KEYBOARD_EVENTS | FOCUS_EVENTS | WINDOW_MOVED), + popup_menu_shown(false) + { + + menu_.set_on_hide_handler(*this,&popup_menu_region::on_menu_becomes_hidden); + enable_events(); + } + +// ---------------------------------------------------------------------------------------- + + popup_menu_region:: + ~popup_menu_region( + ) + { + disable_events(); + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu_region:: + set_size ( + unsigned long width, + unsigned long height + ) + { + auto_mutex M(m); + rect = resize_rect(rect,width,height); + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu_region:: + set_rect ( + const rectangle& new_rect + ) + { + auto_mutex M(m); + rect = new_rect; + } + +// ---------------------------------------------------------------------------------------- + + popup_menu& popup_menu_region:: + menu ( + ) + { + return menu_; + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu_region:: + hide ( + ) + { + auto_mutex M(m); + drawable::hide(); + menu_.hide(); + popup_menu_shown = false; + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu_region:: + disable ( + ) + { + auto_mutex M(m); + drawable::disable(); + menu_.hide(); + popup_menu_shown = false; + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu_region:: + on_keydown ( + unsigned long key, + bool is_printable, + unsigned long state + ) + { + if (enabled && !hidden && popup_menu_shown) + { + menu_.forwarded_on_keydown(key, is_printable, state); + } + else if (popup_menu_shown) + { + menu_.hide(); + popup_menu_shown = false; + } + + if (key == (unsigned long)base_window::KEY_ESC) + { + menu_.hide(); + popup_menu_shown = false; + } + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu_region:: + on_menu_becomes_hidden ( + ) + { + popup_menu_shown = false; + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu_region:: + on_focus_lost ( + ) + { + if (popup_menu_shown) + { + menu_.hide(); + popup_menu_shown = false; + } + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu_region:: + on_focus_gained ( + ) + { + if (popup_menu_shown) + { + menu_.hide(); + popup_menu_shown = false; + } + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu_region:: + on_window_moved( + ) + { + if (popup_menu_shown) + { + menu_.hide(); + popup_menu_shown = false; + } + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu_region:: + on_mouse_down ( + unsigned long btn, + unsigned long , + long x, + long y, + bool + ) + { + if (enabled && !hidden && rect.contains(x,y) && btn == base_window::RIGHT) + { + long orig_x, orig_y; + parent.get_pos(orig_x, orig_y); + menu_.set_pos(orig_x+x, orig_y+y); + menu_.show(); + popup_menu_shown = true; + } + else if (popup_menu_shown) + { + menu_.hide(); + popup_menu_shown = false; + } + } + +// ---------------------------------------------------------------------------------------- + + void popup_menu_region:: + draw ( + const canvas& + ) const + { + } + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_BASE_WIDGETs_CPP_ + |