iOS: Touch interface implemented, and a RMB emulation of such on PC.

This commit is contained in:
Victor Sergienko 2018-06-18 20:54:34 -07:00 committed by Jyrki Vesterinen
parent ea2e16d251
commit aedc081b2e
14 changed files with 533 additions and 39 deletions

View file

@ -40,6 +40,12 @@
command="deselecthex"
mouse=0
[/hotkey]
[hotkey]
button=1
command="selectmoveaction"
# Which means "touch"
mouse=255
[/hotkey]
[hotkey]
command=aiformula

View file

@ -27,6 +27,7 @@
#include <boost/algorithm/string/replace.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <SDL_platform.h>
static lg::log_domain log_cache("cache");
#define ERR_CACHE LOG_STREAM(err, log_cache)
@ -45,6 +46,10 @@ void add_builtin_defines(preproc_map& target)
target["APPLE"] = preproc_define();
#endif
#if defined(MOUSE_TOUCH_EMULATION) || defined(__IPHONEOS__)
target["IPHONEOS"] = preproc_define();
#endif
target["WESNOTH_VERSION"] = preproc_define(game_config::wesnoth_version.str());
}

View file

@ -56,6 +56,8 @@ void controller_base::handle_event(const SDL_Event& event)
events::mouse_handler_base& mh_base = get_mouse_handler_base();
SDL_Event new_event = {};
switch(event.type) {
case SDL_TEXTINPUT:
if(have_keyboard_focus()) {
@ -96,33 +98,47 @@ void controller_base::handle_event(const SDL_Event& event)
break;
case SDL_JOYBUTTONDOWN:
process_keydown_event(event);
hotkey::jbutton_event(event, get_hotkey_command_executor());
break;
case SDL_JOYHATMOTION:
process_keydown_event(event);
hotkey::jhat_event(event, get_hotkey_command_executor());
break;
case SDL_MOUSEMOTION:
// Ignore old mouse motion events in the event queue
SDL_Event new_event;
if(SDL_PeepEvents(&new_event, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) {
while(SDL_PeepEvents(&new_event, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) {
};
mh_base.mouse_motion_event(new_event.motion, is_browsing());
if(new_event.motion.which != SDL_TOUCH_MOUSEID) {
mh_base.mouse_motion_event(new_event.motion, is_browsing());
}
} else {
mh_base.mouse_motion_event(event.motion, is_browsing());
if(new_event.motion.which != SDL_TOUCH_MOUSEID) {
mh_base.mouse_motion_event(event.motion, is_browsing());
}
}
break;
case SDL_FINGERMOTION:
if(SDL_PeepEvents(&new_event, 1, SDL_GETEVENT, SDL_FINGERMOTION, SDL_FINGERMOTION) > 0) {
while(SDL_PeepEvents(&new_event, 1, SDL_GETEVENT, SDL_FINGERMOTION, SDL_FINGERMOTION) > 0) {
};
mh_base.touch_motion_event(new_event.tfinger, is_browsing());
} else {
mh_base.touch_motion_event(event.tfinger, is_browsing());
}
break;
case SDL_MOUSEBUTTONDOWN:
process_keydown_event(event);
mh_base.mouse_press(event.button, is_browsing());
hotkey::mbutton_event(event, get_hotkey_command_executor());
break;
case SDL_FINGERDOWN:
// handled by mouse case
break;
case SDL_MOUSEBUTTONUP:
mh_base.mouse_press(event.button, is_browsing());
if(mh_base.get_show_menu()) {
@ -131,6 +147,10 @@ void controller_base::handle_event(const SDL_Event& event)
}
break;
case SDL_FINGERUP:
// handled by mouse case
break;
case SDL_MOUSEWHEEL:
#if defined(_WIN32) || defined(__APPLE__)
mh_base.mouse_wheel(-event.wheel.x, event.wheel.y, is_browsing());
@ -139,6 +159,8 @@ void controller_base::handle_event(const SDL_Event& event)
#endif
break;
// TODO: Support finger specifically, like pan the map. For now, SDL's "shadow mouse" events will do.
case SDL_MULTIGESTURE:
default:
break;
}

View file

@ -1290,6 +1290,11 @@ void editor_controller::mouse_motion(int x, int y, const bool /*browse*/,
gui().highlight_hex(hex_clicked);
}
void editor_controller::touch_motion(int /* x */, int /* y */, const bool /* browse */, bool /* update */, map_location /* new_loc */)
{
// Not implemented at all. Sorry, it's a very low priority for iOS port.
}
bool editor_controller::allow_mouse_wheel_scroll(int x, int y)
{
return get_current_map_context().map().on_board_with_border(gui().hex_clicked_on(x,y));

View file

@ -154,6 +154,7 @@ class editor_controller : public controller_base,
/* mouse_handler_base overrides */
void mouse_motion(int x, int y, const bool browse, bool update, map_location new_loc = map_location::null_location()) override;
void touch_motion(int x, int y, const bool browse, bool update=false, map_location new_loc = map_location::null_location()) override;
editor_display& gui() override { return *gui_; }
const editor_display& gui() const override { return *gui_; }
bool allow_mouse_wheel_scroll(int x, int y) override;

View file

@ -504,11 +504,69 @@ void pump()
events.erase(first_draw_event + 1, events.end());
}
for(const SDL_Event& event : events) {
for(SDL_Event& event : events) {
for(context& c : event_contexts) {
c.add_staging_handlers();
}
#ifdef MOUSE_TOUCH_EMULATION
switch (event.type) {
// TODO: Implement SDL_MULTIGESTURE. Some day.
case SDL_MOUSEMOTION:
if(event.motion.which != SDL_TOUCH_MOUSEID && event.motion.state == 0) {
return;
}
if(event.motion.state & SDL_BUTTON(SDL_BUTTON_RIGHT))
{
SDL_Rect r = CVideo::get_singleton().screen_area();
// TODO: Check if SDL_FINGERMOTION is actually signaled for COMPLETE motions (I doubt, but tbs)
SDL_Event touch_event;
touch_event.type = SDL_FINGERMOTION;
touch_event.tfinger.type = SDL_FINGERMOTION;
touch_event.tfinger.timestamp = event.motion.timestamp;
touch_event.tfinger.touchId = 1;
touch_event.tfinger.fingerId = 1;
touch_event.tfinger.dx = static_cast<float>(event.motion.xrel) / r.w;
touch_event.tfinger.dy = static_cast<float>(event.motion.yrel) / r.h;
touch_event.tfinger.x = static_cast<float>(event.motion.x) / r.w;
touch_event.tfinger.y = static_cast<float>(event.motion.y) / r.h;
touch_event.tfinger.pressure = 1;
::SDL_PushEvent(&touch_event);
event.motion.state = SDL_BUTTON(SDL_BUTTON_LEFT);
event.motion.which = SDL_TOUCH_MOUSEID;
}
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
if(event.button.button == SDL_BUTTON_RIGHT)
{
event.button.button = SDL_BUTTON_LEFT;
event.button.which = SDL_TOUCH_MOUSEID;
SDL_Rect r = CVideo::get_singleton().screen_area();
SDL_Event touch_event;
touch_event.type = (event.type == SDL_MOUSEBUTTONDOWN) ? SDL_FINGERDOWN : SDL_FINGERUP;
touch_event.tfinger.type = touch_event.type;
touch_event.tfinger.timestamp = event.button.timestamp;
touch_event.tfinger.touchId = 1;
touch_event.tfinger.fingerId = 1;
touch_event.tfinger.dx = 0;
touch_event.tfinger.dy = 0;
touch_event.tfinger.x = static_cast<float>(event.button.x) / r.w;
touch_event.tfinger.y = static_cast<float>(event.button.y) / r.h;
touch_event.tfinger.pressure = 1;
::SDL_PushEvent(&touch_event);
}
break;
default:
break;
}
#endif
switch(event.type) {
case SDL_WINDOWEVENT:
switch(event.window.event) {

View file

@ -379,17 +379,30 @@ void sdl_event_handler::handle_event(const SDL_Event& event)
return;
}
Uint8 button = event.button.button;
CVideo& video = dynamic_cast<window&>(*dispatchers_.back()).video();
switch(event.type) {
case SDL_MOUSEMOTION:
mouse(SDL_MOUSE_MOTION, {event.motion.x, event.motion.y});
#ifdef MOUSE_TOUCH_EMULATION
// There's no finger motion when it's not down.
if (event.motion.state != 0)
#endif
{
mouse(SDL_MOUSE_MOTION, {event.motion.x, event.motion.y});
}
break;
case SDL_MOUSEBUTTONDOWN:
mouse_button_down({event.button.x, event.button.y}, event.button.button);
{
mouse_button_down({event.button.x, event.button.y}, button);
}
break;
case SDL_MOUSEBUTTONUP:
mouse_button_up({event.button.x, event.button.y}, event.button.button);
{
mouse_button_up({event.button.x, event.button.y}, button);
}
break;
case SDL_MOUSEWHEEL:
@ -465,20 +478,33 @@ void sdl_event_handler::handle_event(const SDL_Event& event)
break;
case SDL_FINGERMOTION:
touch_motion(point(event.tfinger.x, event.tfinger.y), point(event.tfinger.dx, event.tfinger.dy));
{
SDL_Rect r = video.screen_area();
touch_motion(point(event.tfinger.x * r.w, event.tfinger.y * r.h),
point(event.tfinger.dx * r.w, event.tfinger.dy * r.h));
}
break;
case SDL_FINGERUP:
touch_up(point(event.tfinger.x, event.tfinger.y));
{
SDL_Rect r = video.screen_area();
touch_up(point(event.tfinger.x * r.w, event.tfinger.y * r.h));
}
break;
case SDL_FINGERDOWN:
touch_down(point(event.tfinger.x, event.tfinger.y));
{
SDL_Rect r = video.screen_area();
touch_down(point(event.tfinger.x * r.w, event.tfinger.y * r.h));
}
break;
case SDL_MULTIGESTURE:
touch_multi_gesture(point(event.mgesture.x, event.mgesture.y),
event.mgesture.dTheta, event.mgesture.dDist, event.mgesture.numFingers);
{
SDL_Rect r = video.screen_area();
touch_multi_gesture(point(event.mgesture.x * r.w, event.mgesture.y * r.h),
event.mgesture.dTheta, event.mgesture.dDist, event.mgesture.numFingers);
}
break;
#if(defined(_X11) && !defined(__APPLE__)) || defined(_WIN32)

View file

@ -39,7 +39,6 @@ void hotkey_bind::pre_show(window& window)
window.connect_signal<event::SDL_RAW_EVENT>(
std::bind(&hotkey_bind::sdl_event_callback, this, std::ref(window), _5),
event::dispatcher::front_child);
}
void hotkey_bind::sdl_event_callback(window& win, const SDL_Event &event)

View file

@ -593,7 +593,7 @@ void command_executor::queue_command(const SDL_Event& event, int index)
bool keypress = (event.type == SDL_KEYDOWN || event.type == SDL_TEXTINPUT) &&
!press_event_sent_;
bool press = keypress ||
(event.type == SDL_JOYBUTTONDOWN || event.type == SDL_MOUSEBUTTONDOWN);
(event.type == SDL_JOYBUTTONDOWN || event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_FINGERDOWN);
bool release = event.type == SDL_KEYUP;
if(press) {
LOG_HK << "sending press event (keypress = " <<

View file

@ -40,6 +40,11 @@ namespace hotkey {
hotkey_list hotkeys_;
config default_hotkey_cfg_;
namespace {
const int TOUCH_MOUSE_INDEX = 255;
const char TOUCH_MOUSE_STRING[] = "255";
};
static unsigned int sdl_get_mods()
{
unsigned int mods;
@ -196,7 +201,11 @@ hotkey_ptr load_from_config(const config& cfg)
if (!mouse_cfg.empty()) {
auto mouse = std::make_shared<hotkey_mouse>();
base = std::dynamic_pointer_cast<hotkey_base>(mouse);
mouse->set_button(cfg["button"].to_int());
if (mouse_cfg == TOUCH_MOUSE_STRING) {
mouse->set_button(TOUCH_MOUSE_INDEX);
} else {
mouse->set_button(cfg["button"].to_int());
}
}
// TODO: add joystick support back
#if 0
@ -254,7 +263,10 @@ hotkey_ptr load_from_config(const config& cfg)
bool hotkey_mouse::matches_helper(const SDL_Event &event) const
{
if (event.type != SDL_MOUSEBUTTONUP && event.type != SDL_MOUSEBUTTONDOWN) {
if (event.type != SDL_MOUSEBUTTONUP
&& event.type != SDL_MOUSEBUTTONDOWN
&& event.type != SDL_FINGERDOWN
&& event.type != SDL_FINGERUP) {
return false;
}
@ -263,11 +275,11 @@ bool hotkey_mouse::matches_helper(const SDL_Event &event) const
return false;
}
if (event.button.button != button_) {
return false;
if (event.button.which == SDL_TOUCH_MOUSEID) {
return button_ == TOUCH_MOUSE_INDEX;
}
return true;
return event.button.button == button_;
}
const std::string hotkey_mouse::get_name_helper() const

View file

@ -89,9 +89,288 @@ void mouse_handler::set_side(int side_number)
int mouse_handler::drag_threshold() const
{
// TODO: Use physical screen size.
return 14;
}
void mouse_handler::touch_motion(int x, int y, const bool browse, bool update, map_location new_hex)
{
// Frankensteining from mouse_motion(), as it has a lot in common, but a lot of differences too.
// Copy-pasted from everywhere. TODO: generalize the two.
SDL_GetMouseState(&x,&y);
// This is from mouse_handler_base::mouse_motion_default()
tooltips::process(x, y);
if(simple_warp_) {
return;
}
if(minimap_scrolling_) {
const map_location& mini_loc = gui().minimap_location_on(x,y);
if(mini_loc.valid()) {
if(mini_loc != last_hex_) {
last_hex_ = mini_loc;
gui().scroll_to_tile(mini_loc,display::WARP,false);
}
return;
} else {
// clicking outside of the minimap will end minimap scrolling
minimap_scrolling_ = false;
}
}
// Fire the drag & drop only after minimal drag distance
// While we check the mouse buttons state, we also grab fresh position data.
int mx = drag_from_x_; // some default value to prevent unlikely SDL bug
int my = drag_from_y_;
if(is_dragging() && !dragging_started_) {
if(dragging_touch_) {
SDL_GetMouseState(&mx, &my);
const double drag_distance = std::pow(static_cast<double>(drag_from_x_- mx), 2)
+ std::pow(static_cast<double>(drag_from_y_- my), 2);
if(drag_distance > drag_threshold()*drag_threshold()) {
dragging_started_ = true;
// cursor::set_dragging(true);
}
}
}
// my own panning...
bool selected_hex_has_unit = find_unit(selected_hex_).valid();
if(!selected_hex_has_unit && is_dragging() && dragging_started_) {
// Here, naive pan. In general, is it a problem that panning is synchronous?
// Looks like it can do frameskips.
// but does it use CPU? I think on iOS GPU is much more appropriate for panning.
// What does SDL2 have to offer?
SDL_GetMouseState(&mx, &my);
int dx = drag_from_x_ - mx;
int dy = drag_from_y_ - my;
gui().scroll(dx, dy);
drag_from_x_ = mx;
drag_from_y_ = my;
return;
}
// now copy-pasting mouse_handler::mouse_motion()
game_board & board = pc_.gamestate().board_;
if(new_hex == map_location::null_location())
new_hex = gui().hex_clicked_on(x,y);
if(new_hex != last_hex_) {
update = true;
if( pc_.get_map_const().on_board(last_hex_) ) {
// we store the previous hexes used to propose attack direction
previous_hex_ = last_hex_;
// the hex of the selected unit is also "free"
{ // start planned unit map scope
wb::future_map_if_active raii;
if(last_hex_ == selected_hex_ || !find_unit(last_hex_)) {
previous_free_hex_ = last_hex_;
}
} // end planned unit map scope
}
last_hex_ = new_hex;
}
if(reachmap_invalid_) update = true;
if(!update) return;
if(reachmap_invalid_) {
reachmap_invalid_ = false;
if(!current_paths_.destinations.empty() && !show_partial_move_) {
{ // start planned unit map scope
wb::future_map_if_active planned_unit_map;
selected_hex_has_unit = find_unit(selected_hex_).valid();
} // end planned unit map scope
if(selected_hex_.valid() && selected_hex_has_unit ) {
// FIXME: vic: why doesn't this trigger when touch-dragging an unselected unit?
// reselect the unit without firing events (updates current_paths_)
select_hex(selected_hex_, true);
}
// we do never deselect here, mainly because of canceled attack-move
}
}
// reset current_route_ and current_paths if not valid anymore
// we do it before cursor selection, because it uses current_paths_
if( !pc_.get_map_const().on_board(new_hex) ) {
current_route_.steps.clear();
gui().set_route(nullptr);
pc_.get_whiteboard()->erase_temp_move();
}
if(unselected_paths_) {
unselected_paths_ = false;
current_paths_ = pathfind::paths();
gui().unhighlight_reach();
} else if(over_route_) {
over_route_ = false;
current_route_.steps.clear();
gui().set_route(nullptr);
pc_.get_whiteboard()->erase_temp_move();
}
gui().highlight_hex(new_hex);
pc_.get_whiteboard()->on_mouseover_change(new_hex);
unit_map::iterator selected_unit;
unit_map::iterator mouseover_unit;
map_location attack_from;
{ // start planned unit map scope
wb::future_map_if_active planned_unit_map;
selected_unit = find_unit(selected_hex_);
mouseover_unit = find_unit(new_hex);
// we search if there is an attack possibility and where
attack_from = current_unit_attacks_from(new_hex);
//see if we should show the normal cursor, the movement cursor, or
//the attack cursor
//If the cursor is on WAIT, we don't change it and let the setter
//of this state end it
if (cursor::get() != cursor::WAIT) {
if (selected_unit &&
selected_unit->side() == side_num_ &&
!selected_unit->incapacitated() && !browse)
{
if (attack_from.valid()) {
cursor::set(dragging_started_ ? cursor::ATTACK_DRAG : cursor::ATTACK);
}
else if (!mouseover_unit &&
current_paths_.destinations.contains(new_hex))
{
// Is this where left-drag cursor changes? Test.
cursor::set(dragging_started_ ? cursor::MOVE_DRAG : cursor::MOVE);
} else {
// selected unit can't attack or move there
cursor::set(cursor::NORMAL);
}
} else {
// no selected unit or we can't move it
if ( selected_hex_.valid() && mouseover_unit
&& mouseover_unit->side() == side_num_ ) {
// empty hex field selected and unit on our site under the cursor
cursor::set(dragging_started_ ? cursor::MOVE_DRAG : cursor::MOVE);
} else {
cursor::set(cursor::NORMAL);
}
}
}
} // end planned unit map scope
// show (or cancel) the attack direction indicator
if(attack_from.valid() && (!browse || pc_.get_whiteboard()->is_active())) {
gui().set_attack_indicator(attack_from, new_hex);
} else {
gui().clear_attack_indicator();
}
unit_ptr un; //will later point to unit at mouseover_hex_
// the destination is the pointed hex or the adjacent hex
// used to attack it
map_location dest;
unit_map::const_iterator dest_un;
{ // start planned unit map scope
wb::future_map_if_active raii;
if (attack_from.valid()) {
dest = attack_from;
dest_un = find_unit(dest);
} else {
dest = new_hex;
dest_un = find_unit(new_hex);
}
if(dest == selected_hex_ || dest_un) {
current_route_.steps.clear();
gui().set_route(nullptr);
pc_.get_whiteboard()->erase_temp_move();
}
else if (!current_paths_.destinations.empty() &&
board.map().on_board(selected_hex_) && board.map().on_board(new_hex))
{
if (selected_unit && !selected_unit->incapacitated()) {
// Show the route from selected unit to mouseover hex
current_route_ = get_route(&*selected_unit, dest, viewing_team());
pc_.get_whiteboard()->create_temp_move();
if(!browse) {
gui().set_route(&current_route_);
}
}
}
if(board.map().on_board(selected_hex_)
&& !selected_unit
&& mouseover_unit.valid()
&& mouseover_unit) {
// Show the route from selected hex to mouseover unit
current_route_ = get_route(&*mouseover_unit, selected_hex_, viewing_team());
pc_.get_whiteboard()->create_temp_move();
if(!browse) {
gui().set_route(&current_route_);
}
} else if (!selected_unit) {
current_route_.steps.clear();
gui().set_route(nullptr);
pc_.get_whiteboard()->erase_temp_move();
}
unit_map::iterator iter = mouseover_unit;
if (iter)
un = iter.get_shared_ptr();
else
un.reset();
} //end planned unit map scope
if( (!selected_hex_.valid()) && un && current_paths_.destinations.empty() &&
!gui().fogged(un->get_location()))
{
if (un->side() == side_num_) {
//unit is on our team, show path if the unit has one
const map_location go_to = un->get_goto();
if(board.map().on_board(go_to)) {
pathfind::marked_route route;
{ // start planned unit map scope
wb::future_map_if_active raii;
route = get_route(un.get(), go_to, current_team());
} // end planned unit map scope
gui().set_route(&route);
}
over_route_ = true;
wb::future_map_if_active raii;
current_paths_ = pathfind::paths(*un, false, true,
viewing_team(), path_turns_);
} else {
//unit under cursor is not on our team
//Note: planned unit map must be activated after this is done,
//since the future state includes changes to units' movement.
unit_movement_resetter move_reset(*un);
wb::future_map_if_active raii;
current_paths_ = pathfind::paths(*un, false, true,
viewing_team(), path_turns_);
}
unselected_paths_ = true;
gui().highlight_reach(current_paths_);
}
}
void mouse_handler::mouse_motion(int x, int y, const bool browse, bool update, map_location new_hex)
{
// we ignore the position coming from event handler
@ -235,8 +514,8 @@ void mouse_handler::mouse_motion(int x, int y, const bool browse, bool update, m
// used to attack it
map_location dest;
unit_map::const_iterator dest_un;
{ // start planned unit map scope
/* start planned unit map scope*/
{
wb::future_map_if_active raii;
if(attack_from.valid()) {
dest = attack_from;
@ -279,13 +558,12 @@ void mouse_handler::mouse_motion(int x, int y, const bool browse, bool update, m
pc_.get_whiteboard()->erase_temp_move();
}
unit_map::iterator iter = mouseover_unit;
if(iter) {
un = iter.get_shared_ptr();
if(mouseover_unit) {
un = mouseover_unit.get_shared_ptr();
} else {
un.reset();
}
} // end planned unit map scope
} /*end planned unit map scope*/
if((!selected_hex_.valid()) && un && current_paths_.destinations.empty() && !gui().fogged(un->get_location())) {
/*

View file

@ -130,6 +130,8 @@ protected:
// bool left_click(int x, int y, const bool browse);
bool move_unit_along_current_route();
void touch_motion(int x, int y, const bool browse, bool update=false, map_location loc = map_location::null_location());
void save_whiteboard_attack(const map_location& attacker_loc, const map_location& defender_loc, int weapon_choice);
// fill weapon choices into bc_vector

View file

@ -54,6 +54,7 @@ mouse_handler_base::mouse_handler_base()
, dragging_left_(false)
, dragging_started_(false)
, dragging_right_(false)
, dragging_touch_(false)
, drag_from_x_(0)
, drag_from_y_(0)
, drag_from_hex_()
@ -67,7 +68,7 @@ mouse_handler_base::mouse_handler_base()
bool mouse_handler_base::is_dragging() const
{
return dragging_left_ || dragging_right_;
return dragging_left_ || dragging_right_ || dragging_touch_;
}
void mouse_handler_base::mouse_motion_event(const SDL_MouseMotionEvent& event, const bool browse)
@ -75,6 +76,11 @@ void mouse_handler_base::mouse_motion_event(const SDL_MouseMotionEvent& event, c
mouse_motion(event.x, event.y, browse);
}
void mouse_handler_base::touch_motion_event(const SDL_TouchFingerEvent& event, const bool browse)
{
touch_motion(event.x, event.y, browse);
}
void mouse_handler_base::mouse_update(const bool browse, map_location loc)
{
int x, y;
@ -94,8 +100,7 @@ bool mouse_handler_base::mouse_motion_default(int x, int y, bool /*update*/)
// if the game is run in a window, we could miss a LMB/MMB up event
// if it occurs outside our window.
// thus, we need to check if the LMB/MMB is still down
minimap_scrolling_ = ((SDL_GetMouseState(nullptr, nullptr) & (SDL_BUTTON(1) | SDL_BUTTON(2))) != 0);
minimap_scrolling_ = ((SDL_GetMouseState(nullptr, nullptr) & (SDL_BUTTON(SDL_BUTTON_LEFT) | SDL_BUTTON(SDL_BUTTON_MIDDLE))) != 0);
if(minimap_scrolling_) {
const map_location& loc = gui().minimap_location_on(x, y);
if(loc.valid()) {
@ -120,12 +125,19 @@ bool mouse_handler_base::mouse_motion_default(int x, int y, bool /*update*/)
int my = drag_from_y_;
if(is_dragging() && !dragging_started_) {
if((dragging_left_ && (SDL_GetMouseState(&mx, &my) & SDL_BUTTON_LEFT) != 0) ||
(dragging_right_ && (SDL_GetMouseState(&mx, &my) & SDL_BUTTON_RIGHT) != 0))
Uint32 mouse_state = dragging_left_ || dragging_right_ ? SDL_GetMouseState(&mx, &my) : 0;
#ifdef MOUSE_TOUCH_EMULATION
if(dragging_left_ && (mouse_state & SDL_BUTTON(SDL_BUTTON_RIGHT))) {
// Monkey-patch touch controls again to make them look like left button.
mouse_state = SDL_BUTTON(SDL_BUTTON_LEFT);
}
#endif
if((dragging_left_ && (mouse_state & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0) ||
(dragging_right_ && (mouse_state & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0))
{
const double drag_distance =
std::pow(static_cast<double>(drag_from_x_ - mx), 2) +
std::pow(static_cast<double>(drag_from_y_ - my), 2);
std::pow(static_cast<double>(drag_from_x_- mx), 2) +
std::pow(static_cast<double>(drag_from_y_- my), 2);
if(drag_distance > drag_threshold() * drag_threshold()) {
dragging_started_ = true;
@ -147,7 +159,35 @@ void mouse_handler_base::mouse_press(const SDL_MouseButtonEvent& event, const bo
map_location loc = gui().hex_clicked_on(event.x, event.y);
mouse_update(browse, loc);
if(is_left_click(event)) {
static time_t touch_timestamp = 0;
if(is_touch_click(event)) {
if (event.state == SDL_PRESSED) {
cancel_dragging();
touch_timestamp = time(NULL);
init_dragging(dragging_touch_);
left_click(event.x, event.y, browse);
} else if (event.state == SDL_RELEASED) {
minimap_scrolling_ = false;
if (!dragging_started_ && touch_timestamp > 0) {
time_t dt = clock() - touch_timestamp;
// I couldn't make this work. Sorry for some C.
// auto dt_cpp = high_resolution_clock::now() - touch_timestamp_cpp;
// auto dt2 = duration_cast<milliseconds>(dt_cpp);
// auto menu_hold = milliseconds(300);
// if (dt2 > menu_hold) {
if (dt > CLOCKS_PER_SEC * 3 / 10) {
right_click(event.x, event.y, browse); // show_menu_ = true;
}
} else {
touch_timestamp = 0;
}
clear_dragging(event, browse);
left_mouse_up(event.x, event.y, browse);
}
} else if(is_left_click(event)) {
if(event.state == SDL_PRESSED) {
cancel_dragging();
init_dragging(dragging_left_);
@ -192,8 +232,7 @@ void mouse_handler_base::mouse_press(const SDL_MouseButtonEvent& event, const bo
scroll_started_ = false;
}
}
if(!dragging_left_ && !dragging_right_ && dragging_started_) {
if(!dragging_left_ && !dragging_right_ && !dragging_touch_ && dragging_started_) {
dragging_started_ = false;
cursor::set_dragging(false);
}
@ -203,6 +242,14 @@ void mouse_handler_base::mouse_press(const SDL_MouseButtonEvent& event, const bo
bool mouse_handler_base::is_left_click(const SDL_MouseButtonEvent& event) const
{
#ifdef MOUSE_TOUCH_EMULATION
if(event.button == SDL_BUTTON_RIGHT) {
return true;
}
#endif
if(event.which == SDL_TOUCH_MOUSEID) {
return false;
}
return event.button == SDL_BUTTON_LEFT && !command_active();
}
@ -213,7 +260,21 @@ bool mouse_handler_base::is_middle_click(const SDL_MouseButtonEvent& event) cons
bool mouse_handler_base::is_right_click(const SDL_MouseButtonEvent& event) const
{
return event.button == SDL_BUTTON_RIGHT || (event.button == SDL_BUTTON_LEFT && command_active());
#ifdef MOUSE_TOUCH_EMULATION
(void) event;
return false;
#else
if(event.which == SDL_TOUCH_MOUSEID) {
return false;
}
return event.button == SDL_BUTTON_RIGHT
|| (event.button == SDL_BUTTON_LEFT && command_active());
#endif
}
bool mouse_handler_base::is_touch_click(const SDL_MouseButtonEvent& event) const
{
return event.which == SDL_TOUCH_MOUSEID;
}
bool mouse_handler_base::left_click(int x, int y, const bool /*browse*/)
@ -301,6 +362,7 @@ void mouse_handler_base::cancel_dragging()
{
dragging_started_ = false;
dragging_left_ = false;
dragging_touch_ = false;
dragging_right_ = false;
cursor::set_dragging(false);
}
@ -314,6 +376,13 @@ void mouse_handler_base::clear_dragging(const SDL_MouseButtonEvent& event, bool
if(dragging_started_) {
dragging_started_ = false;
if(dragging_touch_) {
dragging_touch_ = false;
// Maybe to do: create touch_drag_end(). Do panning and what else there. OTOH, it's fine now.
left_drag_end(event.x, event.y, browse);
}
if(dragging_left_) {
dragging_left_ = false;
left_drag_end(event.x, event.y, browse);
@ -326,6 +395,7 @@ void mouse_handler_base::clear_dragging(const SDL_MouseButtonEvent& event, bool
} else {
dragging_left_ = false;
dragging_right_ = false;
dragging_touch_ = false;
}
}

View file

@ -62,6 +62,8 @@ public:
void mouse_motion_event(const SDL_MouseMotionEvent& event, const bool browse);
void touch_motion_event(const SDL_TouchFingerEvent& event, const bool browse);
/** Update the mouse with a fake mouse motion */
void mouse_update(const bool browse, map_location loc);
@ -85,10 +87,15 @@ public:
int x, int y, const bool browse, bool update = false, map_location new_loc = map_location::null_location())
= 0;
virtual void touch_motion(
int x, int y, const bool browse, bool update = false, map_location new_loc = map_location::null_location())
= 0;
virtual void mouse_press(const SDL_MouseButtonEvent& event, const bool browse);
bool is_left_click(const SDL_MouseButtonEvent& event) const;
bool is_middle_click(const SDL_MouseButtonEvent& event) const;
bool is_right_click(const SDL_MouseButtonEvent& event) const;
bool is_touch_click(const SDL_MouseButtonEvent& event) const;
/** Called when scrolling with the mouse wheel. */
virtual void mouse_wheel(int xscroll, int yscroll, bool browse);
@ -214,6 +221,9 @@ protected:
/** LMB drag init flag */
bool dragging_left_;
/** Finger drag init flag */
bool dragging_touch_;
/** Actual drag flag */
bool dragging_started_;