diff --git a/data/core/hotkeys.cfg b/data/core/hotkeys.cfg index 5b64d59bb68e..0c6097fd5264 100644 --- a/data/core/hotkeys.cfg +++ b/data/core/hotkeys.cfg @@ -40,6 +40,12 @@ command="deselecthex" mouse=0 [/hotkey] +[hotkey] + button=1 + command="selectmoveaction" + # Which means "touch" + mouse=255 +[/hotkey] [hotkey] command=aiformula diff --git a/src/config_cache.cpp b/src/config_cache.cpp index 63c4106e3231..a61a8a97b485 100644 --- a/src/config_cache.cpp +++ b/src/config_cache.cpp @@ -27,6 +27,7 @@ #include #include +#include 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()); } diff --git a/src/controller_base.cpp b/src/controller_base.cpp index b0606efba272..98c792e89332 100644 --- a/src/controller_base.cpp +++ b/src/controller_base.cpp @@ -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 { + 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.mouse_motion_event(event.motion, is_browsing()); + 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; } diff --git a/src/editor/controller/editor_controller.cpp b/src/editor/controller/editor_controller.cpp index 6636ad7d8c1f..ca21e32be856 100644 --- a/src/editor/controller/editor_controller.cpp +++ b/src/editor/controller/editor_controller.cpp @@ -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)); diff --git a/src/editor/controller/editor_controller.hpp b/src/editor/controller/editor_controller.hpp index 845b6aeb9cec..ba04cf82017d 100644 --- a/src/editor/controller/editor_controller.hpp +++ b/src/editor/controller/editor_controller.hpp @@ -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; diff --git a/src/events.cpp b/src/events.cpp index 2ccadce79867..bc218e37be1d 100644 --- a/src/events.cpp +++ b/src/events.cpp @@ -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(event.motion.xrel) / r.w; + touch_event.tfinger.dy = static_cast(event.motion.yrel) / r.h; + touch_event.tfinger.x = static_cast(event.motion.x) / r.w; + touch_event.tfinger.y = static_cast(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(event.button.x) / r.w; + touch_event.tfinger.y = static_cast(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) { diff --git a/src/gui/core/event/handler.cpp b/src/gui/core/event/handler.cpp index e29b8a4fab0a..ed4519572305 100644 --- a/src/gui/core/event/handler.cpp +++ b/src/gui/core/event/handler.cpp @@ -379,17 +379,30 @@ void sdl_event_handler::handle_event(const SDL_Event& event) return; } + Uint8 button = event.button.button; + CVideo& video = dynamic_cast(*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) diff --git a/src/gui/dialogs/hotkey_bind.cpp b/src/gui/dialogs/hotkey_bind.cpp index 97ae05fc270d..0754214800a7 100644 --- a/src/gui/dialogs/hotkey_bind.cpp +++ b/src/gui/dialogs/hotkey_bind.cpp @@ -39,7 +39,6 @@ void hotkey_bind::pre_show(window& window) window.connect_signal( 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) diff --git a/src/hotkey/command_executor.cpp b/src/hotkey/command_executor.cpp index d8d30f121bd0..51e0b4929464 100644 --- a/src/hotkey/command_executor.cpp +++ b/src/hotkey/command_executor.cpp @@ -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 = " << diff --git a/src/hotkey/hotkey_item.cpp b/src/hotkey/hotkey_item.cpp index d033570c2b89..b23228eed11a 100644 --- a/src/hotkey/hotkey_item.cpp +++ b/src/hotkey/hotkey_item.cpp @@ -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(); base = std::dynamic_pointer_cast(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 diff --git a/src/mouse_events.cpp b/src/mouse_events.cpp index 074d6359dded..c5a5140cb8c5 100644 --- a/src/mouse_events.cpp +++ b/src/mouse_events.cpp @@ -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(drag_from_x_- mx), 2) + + std::pow(static_cast(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(¤t_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(¤t_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())) { /* diff --git a/src/mouse_events.hpp b/src/mouse_events.hpp index 693a45ab4918..cf123a7198dd 100644 --- a/src/mouse_events.hpp +++ b/src/mouse_events.hpp @@ -130,6 +130,8 @@ class mouse_handler : public mouse_handler_base { // 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 diff --git a/src/mouse_handler_base.cpp b/src/mouse_handler_base.cpp index d987080ed790..b27de4ec6e0c 100644 --- a/src/mouse_handler_base.cpp +++ b/src/mouse_handler_base.cpp @@ -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(drag_from_x_ - mx), 2) + - std::pow(static_cast(drag_from_y_ - my), 2); + std::pow(static_cast(drag_from_x_- mx), 2) + + std::pow(static_cast(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(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; } } diff --git a/src/mouse_handler_base.hpp b/src/mouse_handler_base.hpp index fd7b329e1607..11b478214c36 100644 --- a/src/mouse_handler_base.hpp +++ b/src/mouse_handler_base.hpp @@ -62,6 +62,8 @@ class mouse_handler_base 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 @@ class mouse_handler_base 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 @@ class mouse_handler_base /** LMB drag init flag */ bool dragging_left_; + /** Finger drag init flag */ + bool dragging_touch_; + /** Actual drag flag */ bool dragging_started_;