/* * A Dragger Implementation * Nana C++ Library(http://www.nanapro.org) * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * * @file: nana/gui/dragger.cpp */ #include #include 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() { //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::release_capture(t.wd); } } 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::set_capture(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::release_capture(arg.window_handle); 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) { for (auto i = triggers_.begin(), end = triggers_.end(); i != end; ++i) { if (i->wd == arg.window_handle) { triggers_.erase(i); API::release_capture(arg.window_handle); return; } } }); triggers_.emplace_back(tg); } private: static void _m_check_restrict_area(nana::point & pos, const nana::size & size, const nana::rectangle& restr_area) { if ((pos.x > 0) && (static_cast(size.width) + pos.x > restr_area.right())) pos.x = restr_area.right() - static_cast(size.width); if (pos.x < restr_area.x) pos.x = restr_area.x; if ((pos.y > 0) && (static_cast(size.height) + pos.y > restr_area.bottom())) pos.y = restr_area.bottom() - static_cast(size.height); if (pos.y < restr_area.y) pos.y = restr_area.y; } private: bool dragging_{ false }; nana::point origin_; std::vector targets_; std::vector triggers_; }; //class dragger dragger::dragger() : impl_(new dragger_impl_t) { } dragger::~dragger() { delete impl_; } dragger::dragger(dragger&& other) : impl_(other.impl_) { other.impl_ = nullptr; } dragger& dragger::operator=(dragger&& other) { if (this != &other) { delete impl_; impl_ = other.impl_; other.impl_ = nullptr; } return *this; } 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