233 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			233 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| *	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 <nana/gui/dragger.hpp>
 | |
| #include <nana/gui/programming_interface.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()
 | |
| 		{
 | |
| 			//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);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		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)
 | |
| 			{
 | |
| 				for (auto i = triggers_.begin(), end = triggers_.end(); i != end; ++i)
 | |
| 				{
 | |
| 					if (i->wd == arg.window_handle)
 | |
| 					{
 | |
| 						triggers_.erase(i);
 | |
| 						API::capture_window(arg.window_handle, false);
 | |
| 						return;
 | |
| 					}
 | |
| 				}
 | |
| 			});
 | |
| 
 | |
| 			triggers_.push_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<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_{ false };
 | |
| 		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_;
 | |
| 		}
 | |
| 
 | |
| 		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
 | 
