nana/source/gui/dragger.cpp
2015-10-07 18:01:46 +08:00

217 lines
4.5 KiB
C++

#include <nana/gui/dragger.hpp>
namespace nana
{
class dragger::dragger_impl_t
{
struct drag_target_t
{
window wd;
rectangle restrict_area;
arrange move_direction;
point origin;
drag_target_t(window w, const rectangle& r, arrange m)
: wd(w), restrict_area(r), move_direction(m)
{}
};
struct trigger_t
{
window wd;
event_handle press;
event_handle over;
event_handle release;
event_handle destroy;
};
public:
dragger_impl_t()
: dragging_(false)
{}
~dragger_impl_t()
{
_m_clear_triggers();
}
void drag_target(window wd, const rectangle& restrict_area, arrange arg)
{
for (auto & td : targets_)
{
if (td.wd == wd)
{
td.restrict_area = restrict_area;
td.move_direction = arg;
return;
}
}
targets_.emplace_back(wd, restrict_area, arg);
}
void remove_target(window wd)
{
for (auto i = targets_.begin(); i != targets_.end(); ++i)
{
if (i->wd == wd)
{
targets_.erase(i);
return;
}
}
}
void trigger(window wd)
{
trigger_t tg;
tg.wd = wd;
auto fn = [this](const arg_mouse& arg)
{
switch (arg.evt_code)
{
case event_code::mouse_down:
dragging_ = true;
API::capture_window(arg.window_handle, true);
origin_ = API::cursor_position();
for (auto & t : targets_)
{
t.origin = API::window_position(t.wd);
window owner = API::get_owner_window(t.wd);
if (owner)
API::calc_screen_point(owner, t.origin);
}
break;
case event_code::mouse_move:
if (dragging_ && arg.left_button)
{
auto pos = API::cursor_position();
pos -= origin_;
for (auto & t : targets_)
{
if (API::is_window_zoomed(t.wd, true) == false)
{
auto owner = API::get_owner_window(t.wd);
auto wdps = t.origin;
if (owner)
API::calc_window_point(owner, wdps);
switch (t.move_direction)
{
case nana::arrange::horizontal:
wdps.x += pos.x;
break;
case nana::arrange::vertical:
wdps.y += pos.y;
break;
default:
wdps += pos;
}
if (!t.restrict_area.empty())
_m_check_restrict_area(wdps, API::window_size(t.wd), t.restrict_area);
API::move_window(t.wd, wdps);
}
}
}
break;
case event_code::mouse_up:
API::capture_window(arg.window_handle, false);
dragging_ = false;
break;
default:
break;
}
};
auto & events = API::events(wd);
tg.press = events.mouse_down.connect(fn);
tg.over = events.mouse_move.connect(fn);
tg.release = events.mouse_up.connect(fn);
tg.destroy = events.destroy.connect([this](const arg_destroy& arg){
_m_destroy(arg.window_handle);
});
triggers_.push_back(tg);
}
private:
void _m_clear_triggers()
{
for(auto & t : triggers_)
{
API::umake_event(t.press);
API::umake_event(t.over);
API::umake_event(t.release);
API::umake_event(t.destroy);
API::capture_window(t.wd, false);
}
triggers_.clear();
}
void _m_destroy(::nana::window wd)
{
for(auto i = triggers_.begin(), end = triggers_.end(); i != end; ++i)
{
if(i->wd == wd)
{
triggers_.erase(i);
API::capture_window(wd, false);
return;
}
}
}
void _m_check_restrict_area(nana::point & pos, const nana::size & size, const nana::rectangle& restr_area)
{
if ((pos.x > 0) && (static_cast<int>(size.width) + pos.x > restr_area.right()))
pos.x = restr_area.right() - static_cast<int>(size.width);
if (pos.x < restr_area.x)
pos.x = restr_area.x;
if ((pos.y > 0) && (static_cast<int>(size.height) + pos.y > restr_area.bottom()))
pos.y = restr_area.bottom() - static_cast<int>(size.height);
if (pos.y < restr_area.y)
pos.y = restr_area.y;
}
private:
bool dragging_;
nana::point origin_;
std::vector<drag_target_t> targets_;
std::vector<trigger_t> triggers_;
};
//class dragger
dragger::dragger()
: impl_(new dragger_impl_t)
{
}
dragger::~dragger()
{
delete impl_;
}
void dragger::target(window wd)
{
impl_->drag_target(wd, rectangle(), nana::arrange::horizontal_vertical);
}
void dragger::target(window wd, const rectangle& restrict_area, nana::arrange arg)
{
impl_->drag_target(wd, restrict_area, arg);
}
void dragger::remove_target(window wd)
{
impl_->remove_target(wd);
}
void dragger::trigger(window tg)
{
impl_->trigger(tg);
}
//end class dragger
}//end namespace nana