Improved android networking
This commit is contained in:
parent
a533494b3a
commit
ad1844adc7
27 changed files with 568 additions and 69 deletions
|
|
@ -56,6 +56,7 @@ set(sources "src/main.cpp"
|
|||
"src/GlobalConfig.cpp"
|
||||
"src/RichTextUserInput.cpp"
|
||||
"src/CustomEvents.cpp"
|
||||
"src/NetThreadManager.cpp"
|
||||
"src/Screens/Screen.cpp"
|
||||
"src/Screens/DrawingProgramScreen.cpp"
|
||||
"src/Screens/DesktopDrawingProgramScreen.cpp"
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ android {
|
|||
ndkVersion = "29.0.14206865"
|
||||
defaultConfig {
|
||||
applicationId 'com.erroratline0.InfiniPaint'
|
||||
minSdkVersion 21
|
||||
minSdkVersion 26
|
||||
targetSdk 35
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
|
|
|||
2
android-project/app/proguard-rules.pro
vendored
2
android-project/app/proguard-rules.pro
vendored
|
|
@ -20,6 +20,8 @@
|
|||
void startTextInput(long, java.lang.String, int, int, int);
|
||||
void updateCursorPos(int, int);
|
||||
void updateTextboxAndCursorPos(java.lang.String, int, int);
|
||||
void startNetworkService();
|
||||
void stopNetworkService();
|
||||
}
|
||||
|
||||
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLActivity {
|
||||
|
|
|
|||
|
|
@ -58,6 +58,9 @@
|
|||
<!-- if you want to connect to the network or internet, uncomment this. -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
|
||||
|
||||
<!-- Create a Java class extending SDLActivity and place it in a
|
||||
directory under app/src/main/java matching the package, e.g. app/src/main/java/com/gamemaker/game/MyGame.java
|
||||
|
||||
|
|
@ -76,6 +79,13 @@
|
|||
<!-- Example of setting SDL hints from AndroidManifest.xml:
|
||||
<meta-data android:name="SDL_ENV.SDL_ANDROID_TRAP_BACK_BUTTON" android:value="0"/>
|
||||
-->
|
||||
<service
|
||||
android:name=".InfiniPaintNetworkService"
|
||||
android:foregroundServiceType="specialUse"
|
||||
android:exported="false">
|
||||
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||
android:value="When connected to a server/hosting a server, keep the connection alive even when the application is in the background."/>
|
||||
</service>
|
||||
|
||||
<activity android:name="com.erroratline0.infinipaint.InfiniPaint"
|
||||
android:label="@string/app_name"
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
package com.erroratline0.infinipaint;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
|
|
@ -142,4 +143,10 @@ public class InfiniPaint extends SDLActivity {
|
|||
static public void updateTextboxAndCursorPos(String str, int selectionBegin, int selectionEnd) {
|
||||
mSingleton.commandHandler.post(new InfiniPaintUpdateTextboxAndCursorPosTask(str, selectionBegin, selectionEnd));
|
||||
}
|
||||
static public void startNetworkService() {
|
||||
mSingleton.startForegroundService(new Intent(mSingleton, InfiniPaintNetworkService.class));
|
||||
}
|
||||
static public void stopNetworkService() {
|
||||
mSingleton.stopService(new Intent(mSingleton, InfiniPaintNetworkService.class));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
package com.erroratline0.infinipaint;
|
||||
|
||||
import android.app.ForegroundServiceStartNotAllowedException;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
|
||||
public class InfiniPaintNetworkService extends Service {
|
||||
public static native void networkServiceStarted();
|
||||
public static native void networkServiceDestroyed();
|
||||
|
||||
private static final String CHANNEL_ID = "NetworkServiceChannel";
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
createNotificationChannel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Notification notification = new Notification.Builder(this, CHANNEL_ID)
|
||||
.setContentTitle("Online canvas")
|
||||
.setContentText("Currently online")
|
||||
.build();
|
||||
startForeground(1, notification);
|
||||
networkServiceStarted();
|
||||
// For now, don't try restarting the connection after we've been disconnected
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
// We don't provide binding, so return null
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
networkServiceDestroyed();
|
||||
}
|
||||
|
||||
private void createNotificationChannel() {
|
||||
NotificationChannel serviceChannel = new NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
"Network Service Channel",
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
);
|
||||
NotificationManager manager = getSystemService(NotificationManager.class);
|
||||
manager.createNotificationChannel(serviceChannel);
|
||||
}
|
||||
}
|
||||
|
|
@ -78,10 +78,7 @@ NetLibrary::LoadP2PSettingsInPathResult NetLibrary::load_p2p_settings_in_path(co
|
|||
return LoadP2PSettingsInPathResult::SUCCESS;
|
||||
}
|
||||
|
||||
void NetLibrary::init(const std::filesystem::path& p2pConfigPath) {
|
||||
if(alreadyInitialized)
|
||||
return;
|
||||
|
||||
void NetLibrary::init_config(const std::filesystem::path& p2pConfigPath) {
|
||||
rtc::InitLogger(rtc::LogLevel::Info, [](rtc::LogLevel level, std::string message) {
|
||||
Logger::get().log("INFO", "[LibDataChannel Log Level " + std::to_string(static_cast<int>(level)) + "] " + message);
|
||||
});
|
||||
|
|
@ -104,7 +101,11 @@ void NetLibrary::init(const std::filesystem::path& p2pConfigPath) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NetLibrary::init_websocket() {
|
||||
if(alreadyInitialized)
|
||||
return;
|
||||
// Not sure why, but TLS verification fails on mac
|
||||
// Should be fixed later, but for now this is fine, as the signaling server doesn't have
|
||||
// critical information
|
||||
|
|
@ -130,7 +131,7 @@ void NetLibrary::init(const std::filesystem::path& p2pConfigPath) {
|
|||
|
||||
ws->onClosed([]() {
|
||||
Logger::get().log("INFO", "Websocket closed");
|
||||
destroy();
|
||||
destroy_websocket();
|
||||
});
|
||||
|
||||
ws->onMessage([wws = make_weak_ptr(ws)](auto data) {
|
||||
|
|
@ -187,7 +188,6 @@ void NetLibrary::init(const std::filesystem::path& p2pConfigPath) {
|
|||
|
||||
Logger::get().log("INFO", "NetLibrary waiting for signaling to be connected...");
|
||||
//wsFuture.get();
|
||||
|
||||
alreadyInitialized = true;
|
||||
}
|
||||
|
||||
|
|
@ -228,7 +228,7 @@ void NetLibrary::register_server(std::shared_ptr<NetServer> server) {
|
|||
|
||||
void NetLibrary::register_client(std::shared_ptr<NetClient> client) {
|
||||
client->localID = Random::get().alphanumeric_str(LOCALID_LEN);
|
||||
std::scoped_lock serverListLock(clientListMutex);
|
||||
std::scoped_lock clientListLock(clientListMutex);
|
||||
clients.emplace_back(client);
|
||||
}
|
||||
|
||||
|
|
@ -429,7 +429,14 @@ MessageOrder NetLibrary::get_message_order(rtc::binary& message) {
|
|||
return u.o;
|
||||
}
|
||||
|
||||
void NetLibrary::destroy() {
|
||||
bool NetLibrary::is_initialized() {
|
||||
return alreadyInitialized;
|
||||
}
|
||||
|
||||
void NetLibrary::destroy_websocket() {
|
||||
if(!alreadyInitialized)
|
||||
return;
|
||||
|
||||
ws = nullptr;
|
||||
{
|
||||
std::scoped_lock peerListLock(peerListMutex);
|
||||
|
|
@ -454,6 +461,6 @@ void NetLibrary::destroy() {
|
|||
servers.clear();
|
||||
}
|
||||
wsPromise = std::promise<void>();
|
||||
//globalID.clear(); // Do we need to do this? (Causes problems when global ID changes from the user's side after they copy a lobby address
|
||||
alreadyInitialized = false;
|
||||
//globalID.clear(); // Do we need to do this? (Causes problems when global ID changes from the user's side after they copy a lobby address
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@ typedef uint8_t NetTypeIDType;
|
|||
|
||||
class NetLibrary {
|
||||
public:
|
||||
static void init(const std::filesystem::path& p2pConfigPath);
|
||||
static void init_config(const std::filesystem::path& p2pConfigPath);
|
||||
static void init_websocket();
|
||||
static void copy_default_p2p_config_to_path(const std::filesystem::path& newP2PConfigPath);
|
||||
static void update();
|
||||
static std::string get_random_server_local_id();
|
||||
|
|
@ -48,7 +49,8 @@ class NetLibrary {
|
|||
static void register_server(std::shared_ptr<NetServer> server);
|
||||
static void register_client(std::shared_ptr<NetClient> client);
|
||||
|
||||
static void destroy();
|
||||
static bool is_initialized();
|
||||
static void destroy_websocket();
|
||||
|
||||
static constexpr size_t LOCALID_LEN = 10;
|
||||
static constexpr size_t GLOBALID_LEN = 40;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include <string>
|
||||
#include <modules/skunicode/include/SkUnicode.h>
|
||||
#include <modules/skunicode/include/SkUnicode_icu.h>
|
||||
#include "NetThreadManager.hpp"
|
||||
|
||||
std::string jstring2string(JNIEnv *env, jstring jStr) {
|
||||
if (!jStr)
|
||||
|
|
@ -94,6 +95,26 @@ namespace AndroidJNICalls {
|
|||
env->DeleteLocalRef(clazz);
|
||||
}
|
||||
|
||||
void startNetworkService() {
|
||||
JNIEnv* env = static_cast<JNIEnv*>(SDL_GetAndroidJNIEnv());
|
||||
jobject activity = (jobject)SDL_GetAndroidActivity();
|
||||
jclass clazz = env->GetObjectClass(activity);
|
||||
jmethodID method_id = env->GetStaticMethodID(clazz, "startNetworkService", "()V");
|
||||
env->CallStaticVoidMethod(clazz, method_id);
|
||||
env->DeleteLocalRef(activity);
|
||||
env->DeleteLocalRef(clazz);
|
||||
}
|
||||
|
||||
void stopNetworkService() {
|
||||
JNIEnv* env = static_cast<JNIEnv*>(SDL_GetAndroidJNIEnv());
|
||||
jobject activity = (jobject)SDL_GetAndroidActivity();
|
||||
jclass clazz = env->GetObjectClass(activity);
|
||||
jmethodID method_id = env->GetStaticMethodID(clazz, "stopNetworkService", "()V");
|
||||
env->CallStaticVoidMethod(clazz, method_id);
|
||||
env->DeleteLocalRef(activity);
|
||||
env->DeleteLocalRef(clazz);
|
||||
}
|
||||
|
||||
void updateModMap(CustomEvents::InputTextBoxID tId, const std::shared_ptr<RichText::TextStyleModifier::ModifierMap>& newModMap) {
|
||||
std::scoped_lock a{textboxMutex};
|
||||
if(tId == textboxID && newModMap && modMap)
|
||||
|
|
@ -309,4 +330,16 @@ Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeSetSel
|
|||
});
|
||||
}
|
||||
|
||||
#endif
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_erroratline0_infinipaint_InfiniPaintNetworkService_networkServiceStarted(JNIEnv *env, jclass clazz) {
|
||||
NetThreadManager::get().init_thread();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_erroratline0_infinipaint_InfiniPaintNetworkService_networkServiceDestroyed(JNIEnv *env, jclass clazz) {
|
||||
NetThreadManager::get().destroy_thread();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -27,6 +27,8 @@ namespace AndroidJNICalls {
|
|||
extern InputManager* globalInputManager;
|
||||
|
||||
void startTextInput(CustomEvents::InputTextBoxID newTextboxID, const std::shared_ptr<RichText::TextBox>& newTextbox, const std::shared_ptr<RichText::TextBox::Cursor>& newCursor, const std::shared_ptr<RichText::TextStyleModifier::ModifierMap>& modMap, int inputType);
|
||||
void startNetworkService();
|
||||
void stopNetworkService();
|
||||
void updateCursorPos(CustomEvents::InputTextBoxID tId, const std::shared_ptr<RichText::TextBox::Cursor>& newCursor);
|
||||
void updateModMap(CustomEvents::InputTextBoxID tId, const std::shared_ptr<RichText::TextStyleModifier::ModifierMap>& newModMap);
|
||||
void updateTextboxAndCursorPos(CustomEvents::InputTextBoxID tId, const std::shared_ptr<RichText::TextBox>& newTextbox, const std::shared_ptr<RichText::TextBox::Cursor>& newCursor);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
#include "Helpers/NetworkingObjects/NetObjTemporaryPtr.decl.hpp"
|
||||
#include <Helpers/NetworkingObjects/NetObjWeakPtr.hpp>
|
||||
#include <Helpers/NetworkingObjects/NetObjGenericSerializedClass.hpp>
|
||||
|
||||
#include "../../Screens/PhoneDrawingProgramScreen.hpp"
|
||||
#include "../../GUIStuff/ElementHelpers/TextLabelHelpers.hpp"
|
||||
#include "../../GUIStuff/ElementHelpers/LayoutHelpers.hpp"
|
||||
#include "../../GUIStuff/ElementHelpers/TextBoxHelpers.hpp"
|
||||
|
|
@ -130,6 +130,16 @@ void GridModifyTool::input_mouse_motion_callback(const InputManager::MouseMotion
|
|||
}
|
||||
}
|
||||
|
||||
Vector4f* GridModifyTool::color_picker_color(Vector4f* oldColor) {
|
||||
NetworkingObjects::NetObjTemporaryPtr<WorldGrid> gLock = grid.lock();
|
||||
if(gLock) {
|
||||
WorldGrid& g = *gLock;
|
||||
if(oldColor == &g.color)
|
||||
return oldColor;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GridModifyTool::gui_toolbox(Toolbar& t) {
|
||||
using namespace GUIStuff;
|
||||
using namespace ElementHelpers;
|
||||
|
|
@ -209,6 +219,10 @@ void GridModifyTool::gui_phone_toolbox(PhoneDrawingProgramScreen& t) {
|
|||
checkbox_boolean_field(gui, "Subdivide outwards", "Subdivide when zooming out", &g.removeDivisionsOutwards, [&] {
|
||||
g.set_remove_divisions_outwards(g.removeDivisionsOutwards);
|
||||
});
|
||||
left_to_right_line_layout(gui, [&]() {
|
||||
t.color_selector_button("Grid Color", &g.color);
|
||||
text_label(gui, "Grid Color");
|
||||
});
|
||||
auto bounded = std::make_shared<bool>(g.bounds.has_value());
|
||||
checkbox_boolean_field(gui, "Bounded", "Bounded", bounded.get(), [&, bounded] {
|
||||
if(*bounded) {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ class GridModifyTool : public DrawingProgramToolBase {
|
|||
GridModifyTool(DrawingProgram& initDrawP);
|
||||
void set_grid(const NetworkingObjects::NetObjWeakPtr<WorldGrid>& newGrid);
|
||||
virtual DrawingProgramToolType get_type() override;
|
||||
virtual Vector4f* color_picker_color(Vector4f* oldColor) override;
|
||||
virtual void gui_phone_toolbox(PhoneDrawingProgramScreen& t) override;
|
||||
virtual void gui_toolbox(Toolbar& t) override;
|
||||
virtual void right_click_popup_gui(Toolbar& t, Vector2f popupPos) override;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include <include/core/SkData.h>
|
||||
#include <include/core/SkSurfaceProps.h>
|
||||
#include "InputManager.hpp"
|
||||
#include "NetThreadManager.hpp"
|
||||
#include <cereal/types/vector.hpp>
|
||||
#include <cereal/types/unordered_map.hpp>
|
||||
#include <Eigen/Core>
|
||||
|
|
@ -90,12 +91,20 @@ void MainProgram::update() {
|
|||
input.update();
|
||||
g.update();
|
||||
screen->update();
|
||||
for(auto& w : worlds)
|
||||
w->update();
|
||||
NetLibrary::update();
|
||||
background_update();
|
||||
post_callback();
|
||||
}
|
||||
|
||||
void MainProgram::background_update() {
|
||||
bool isAnyWorldConnected = false;
|
||||
for(auto& w : worlds) {
|
||||
w->update();
|
||||
isAnyWorldConnected |= ((w->netServer && !w->netServer->is_disconnected()) || (w->netClient && !w->netClient->is_disconnected()));
|
||||
}
|
||||
if(!isAnyWorldConnected)
|
||||
NetThreadManager::get().destroy();
|
||||
}
|
||||
|
||||
bool MainProgram::app_close_requested() {
|
||||
return screen->app_close_requested();
|
||||
}
|
||||
|
|
@ -108,7 +117,7 @@ void MainProgram::update_display_names() {
|
|||
}
|
||||
|
||||
void MainProgram::init_net_library() {
|
||||
NetLibrary::init(conf.configPath / "p2p.json");
|
||||
NetThreadManager::get().init(this);
|
||||
}
|
||||
|
||||
void MainProgram::save_config() {
|
||||
|
|
@ -183,6 +192,7 @@ void MainProgram::load_config() {
|
|||
conf.load_licenses();
|
||||
|
||||
NetLibrary::copy_default_p2p_config_to_path(conf.configPath / "p2p.json");
|
||||
NetLibrary::init_config(conf.configPath / "p2p.json");
|
||||
|
||||
update_display_names();
|
||||
set_vsync_value(conf.vsyncValue);
|
||||
|
|
@ -284,8 +294,8 @@ void MainProgram::switch_to_tab(size_t wIndex) {
|
|||
|
||||
void MainProgram::post_callback() {
|
||||
g.gui.run_post_callback_func();
|
||||
run_new_screen_func();
|
||||
close_set_to_close_tabs();
|
||||
run_new_screen_func();
|
||||
g.gui.layout_if_necessary();
|
||||
}
|
||||
|
||||
|
|
@ -293,6 +303,13 @@ void MainProgram::input_app_about_to_go_to_background_callback() {
|
|||
screen->input_app_about_to_go_to_background_callback();
|
||||
save_config();
|
||||
post_callback();
|
||||
NetThreadManager::get().go_to_background();
|
||||
}
|
||||
|
||||
void MainProgram::input_app_about_to_go_to_foreground_callback() {
|
||||
NetThreadManager::get().go_to_foreground();
|
||||
screen->input_app_about_to_go_to_foreground_callback();
|
||||
post_callback();
|
||||
}
|
||||
|
||||
void MainProgram::input_add_file_to_canvas_callback(const CustomEvents::AddFileToCanvasEvent& addFile) {
|
||||
|
|
@ -488,15 +505,8 @@ void MainProgram::close_set_to_close_tabs() {
|
|||
}
|
||||
return false;
|
||||
});
|
||||
if(worlds.empty())
|
||||
create_new_tab({
|
||||
.isClient = false
|
||||
});
|
||||
else if(world)
|
||||
worldIndex = std::find(worlds.begin(), worlds.end(), world) - worlds.begin();
|
||||
else
|
||||
switch_to_tab(0);
|
||||
tabsToClose.clear();
|
||||
screen->on_tab_close();
|
||||
g.gui.set_to_layout();
|
||||
}
|
||||
}
|
||||
|
|
@ -524,5 +534,5 @@ void MainProgram::early_destroy() {
|
|||
|
||||
MainProgram::~MainProgram() {
|
||||
screen = nullptr; // Destroy screen first to let destructor run
|
||||
NetLibrary::destroy();
|
||||
NetThreadManager::get().destroy();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ class MainProgram {
|
|||
|
||||
MainProgram();
|
||||
void update();
|
||||
void background_update();
|
||||
void draw_world(SkCanvas* canvas, std::shared_ptr<World> worldToDraw, const DrawData& drawData);
|
||||
void draw(SkCanvas* canvas);
|
||||
sk_sp<SkSurface> create_native_surface(Vector2i resolution, bool isMSAA);
|
||||
|
|
@ -156,6 +157,7 @@ class MainProgram {
|
|||
void input_android_text_box_input_callback(const CustomEvents::AndroidTextBoxInputEvent& textboxInput);
|
||||
|
||||
void input_app_about_to_go_to_background_callback();
|
||||
void input_app_about_to_go_to_foreground_callback();
|
||||
void input_global_back_button_callback();
|
||||
bool input_keybind_callback(const Vector2ui32& newKey);
|
||||
void input_drop_file_callback(const InputManager::DropCallbackArgs& drop);
|
||||
|
|
|
|||
79
src/NetThreadManager.cpp
Normal file
79
src/NetThreadManager.cpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#include "NetThreadManager.hpp"
|
||||
#include <Helpers/Networking/NetLibrary.hpp>
|
||||
#include "AndroidJNICalls.hpp"
|
||||
#include "MainProgram.hpp"
|
||||
|
||||
NetThreadManager NetThreadManager::global;
|
||||
|
||||
NetThreadManager& NetThreadManager::get() {
|
||||
return global;
|
||||
}
|
||||
|
||||
void NetThreadManager::init(MainProgram* m) {
|
||||
if(started)
|
||||
return;
|
||||
started = true;
|
||||
main = m;
|
||||
#ifndef __ANDROID__
|
||||
init_thread();
|
||||
#else
|
||||
AndroidJNICalls::startNetworkService();
|
||||
#endif
|
||||
}
|
||||
|
||||
void NetThreadManager::init_thread() {
|
||||
if(t)
|
||||
throw std::runtime_error("[NetThreadManager::init_thread] Thread can't exist when init_thread is called");
|
||||
destroyThread = false;
|
||||
t = std::make_unique<std::thread>([&]{ thread_update(); });
|
||||
}
|
||||
|
||||
void NetThreadManager::thread_update() {
|
||||
NetLibrary::init_websocket();
|
||||
for(;;) {
|
||||
using namespace std::chrono_literals;
|
||||
std::this_thread::sleep_for(20ms);
|
||||
if(destroyThread) {
|
||||
NetLibrary::destroy_websocket();
|
||||
break;
|
||||
}
|
||||
{
|
||||
std::scoped_lock l{backgroundUpdateMutex};
|
||||
if(backgroundUpdate)
|
||||
main->background_update();
|
||||
}
|
||||
NetLibrary::update();
|
||||
}
|
||||
#ifdef __ANDROID__
|
||||
AndroidJNICalls::stopNetworkService();
|
||||
#endif
|
||||
}
|
||||
|
||||
void NetThreadManager::destroy_thread() {
|
||||
if(!t)
|
||||
throw std::runtime_error("[NetThreadManager::init_thread] Thread can't be null when destroy_thread is called");
|
||||
destroyThread = true;
|
||||
t->join();
|
||||
t = nullptr;
|
||||
}
|
||||
|
||||
void NetThreadManager::go_to_background() {
|
||||
std::scoped_lock l{backgroundUpdateMutex};
|
||||
backgroundUpdate = true;
|
||||
}
|
||||
|
||||
void NetThreadManager::go_to_foreground() {
|
||||
std::scoped_lock l{backgroundUpdateMutex};
|
||||
backgroundUpdate = false;
|
||||
}
|
||||
|
||||
void NetThreadManager::destroy() {
|
||||
if(!started)
|
||||
return;
|
||||
started = false;
|
||||
#ifndef __ANDROID__
|
||||
destroy_thread();
|
||||
#else
|
||||
AndroidJNICalls::stopNetworkService();
|
||||
#endif
|
||||
}
|
||||
27
src/NetThreadManager.hpp
Normal file
27
src/NetThreadManager.hpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
|
||||
class MainProgram;
|
||||
|
||||
class NetThreadManager {
|
||||
public:
|
||||
static NetThreadManager& get();
|
||||
void init(MainProgram* m);
|
||||
void thread_update();
|
||||
void destroy();
|
||||
void init_thread();
|
||||
void destroy_thread();
|
||||
void go_to_background();
|
||||
void go_to_foreground();
|
||||
private:
|
||||
static NetThreadManager global;
|
||||
MainProgram* main;
|
||||
std::mutex backgroundUpdateMutex;
|
||||
bool backgroundUpdate = false;
|
||||
std::unique_ptr<std::thread> t;
|
||||
std::atomic<bool> destroyThread = false;
|
||||
|
||||
bool started = false;
|
||||
};
|
||||
|
|
@ -83,3 +83,14 @@ void DesktopDrawingProgramScreen::open_file_selector(const std::string& filePick
|
|||
else
|
||||
toolbar.open_file_selector_non_native(filePickerName, extensionFilters, postSelectionFunc, fileName, isSaving);
|
||||
}
|
||||
|
||||
void DesktopDrawingProgramScreen::on_tab_close() {
|
||||
if(main.worlds.empty())
|
||||
main.create_new_tab({
|
||||
.isClient = false
|
||||
});
|
||||
else if(main.world)
|
||||
main.worldIndex = std::find(main.worlds.begin(), main.worlds.end(), main.world) - main.worlds.begin();
|
||||
else
|
||||
main.switch_to_tab(0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ class DesktopDrawingProgramScreen : public DrawingProgramScreen {
|
|||
virtual void input_key_callback(const InputManager::KeyCallbackArgs& key) override;
|
||||
virtual void input_text_key_callback(const InputManager::KeyCallbackArgs& key) override;
|
||||
virtual void open_file_selector(const std::string& filePickerName, const std::vector<ExtensionFilter>& extensionFilters, OpenFileSelectorCallback postSelectionFunc, const std::string& fileName = "", bool isSaving = false) override;
|
||||
virtual void on_tab_close() override;
|
||||
|
||||
private:
|
||||
Toolbar toolbar;
|
||||
|
|
|
|||
|
|
@ -220,6 +220,9 @@ void FileSelectScreen::main_display() {
|
|||
case SelectedMenu::TRASH:
|
||||
file_view();
|
||||
break;
|
||||
case SelectedMenu::CONNECT:
|
||||
connect_view();
|
||||
break;
|
||||
case SelectedMenu::SETTINGS:
|
||||
settings_view();
|
||||
break;
|
||||
|
|
@ -491,13 +494,16 @@ void FileSelectScreen::title_bar() {
|
|||
});
|
||||
switch(selectedMenu) {
|
||||
case SelectedMenu::FILES:
|
||||
mutable_text_label(gui, "main screen header text", "Files");
|
||||
mutable_text_label(gui, "main screen header text files", "Files");
|
||||
break;
|
||||
case SelectedMenu::TRASH:
|
||||
mutable_text_label(gui, "main screen header text", "Trash");
|
||||
mutable_text_label(gui, "main screen header text trash", "Trash");
|
||||
break;
|
||||
case SelectedMenu::CONNECT:
|
||||
mutable_text_label(gui, "main screen header text connect", "Connect");
|
||||
break;
|
||||
case SelectedMenu::SETTINGS:
|
||||
mutable_text_label(gui, "main screen header text", "Settings");
|
||||
mutable_text_label(gui, "main screen header text settings", "Settings");
|
||||
break;
|
||||
}
|
||||
CLAY_AUTO_ID({
|
||||
|
|
@ -696,6 +702,11 @@ void FileSelectScreen::main_menu() {
|
|||
mainMenuOpenAnim->animation_trigger_reverse();
|
||||
if(mainViewScrollArea) mainViewScrollArea->reset_scroll();
|
||||
});
|
||||
icon_text_transparent_option_selected_button("Connect", "data/icons/network.svg", "Connect", selectedMenu == SelectedMenu::CONNECT, [&] {
|
||||
selectedMenu = SelectedMenu::CONNECT;
|
||||
mainMenuOpenAnim->animation_trigger_reverse();
|
||||
if(mainViewScrollArea) mainViewScrollArea->reset_scroll();
|
||||
});
|
||||
icon_text_transparent_option_selected_button("Settings", "data/icons/RemixIcon/settings-3-line.svg", "Settings", selectedMenu == SelectedMenu::SETTINGS, [&] {
|
||||
selectedMenu = SelectedMenu::SETTINGS;
|
||||
mainMenuOpenAnim->animation_trigger_reverse();
|
||||
|
|
@ -854,6 +865,50 @@ void FileSelectScreen::file_view() {
|
|||
}
|
||||
}
|
||||
|
||||
void FileSelectScreen::connect_view() {
|
||||
auto& gui = main.g.gui;
|
||||
|
||||
mainViewScrollArea = gui.element<ScrollArea>("connect scroll area", ScrollArea::Options{
|
||||
.scrollVertical = true,
|
||||
.clipVertical = true,
|
||||
.scrollbarY = ScrollArea::ScrollbarType::NORMAL,
|
||||
.innerContent = [&] (auto&) {
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {
|
||||
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)},
|
||||
.layoutDirection = CLAY_LEFT_TO_RIGHT
|
||||
},
|
||||
}) {
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {
|
||||
.sizing = {.width = CLAY_SIZING_GROW(0, 300), .height = CLAY_SIZING_GROW(0)},
|
||||
.childGap = gui.io.theme->childGap1,
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM
|
||||
},
|
||||
}) {
|
||||
input_text_field(gui, "Connect lobby field", "Lobby", &connectLobbyStr);
|
||||
text_button(gui, "Connect button", "Connect", {
|
||||
.wide = true,
|
||||
.onClick = [&] {
|
||||
CustomEvents::emit_event<CustomEvents::OpenInfiniPaintFileEvent>({
|
||||
.isClient = true,
|
||||
.netSource = connectLobbyStr
|
||||
});
|
||||
}
|
||||
});
|
||||
text_button(gui, "Paste and Connect button", "Paste & Connect", {
|
||||
.wide = true,
|
||||
.onClick = [&] {
|
||||
connectOnPaste = true;
|
||||
main.input.call_paste(CustomEvents::PasteEvent::DataType::TEXT, { .allowRichText = false });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void FileSelectScreen::settings_view() {
|
||||
auto& gui = main.g.gui;
|
||||
|
||||
|
|
@ -875,6 +930,7 @@ void FileSelectScreen::settings_view() {
|
|||
.layoutDirection = CLAY_TOP_TO_BOTTOM
|
||||
},
|
||||
}) {
|
||||
input_text_field(gui, "display name input", "Display name", &main.conf.displayName);
|
||||
color_picker_button_field(gui, "defaultCanvasBackgroundColor", "Default canvas background color", &main.conf.defaultCanvasBackgroundColor, { .hasAlpha = false });
|
||||
input_scalar_field(gui, "Max GUI Scale", "Max GUI Scale", &main.conf.guiScale, 1.0f, 2.0f, {
|
||||
.decimalPrecision = 1,
|
||||
|
|
@ -926,6 +982,15 @@ void FileSelectScreen::start_edit_mode() {
|
|||
editMode = true;
|
||||
}
|
||||
|
||||
void FileSelectScreen::input_paste_callback(const CustomEvents::PasteEvent& paste) {
|
||||
if(connectOnPaste && paste.type == CustomEvents::PasteEvent::DataType::TEXT) {
|
||||
CustomEvents::emit_event<CustomEvents::OpenInfiniPaintFileEvent>({
|
||||
.isClient = true,
|
||||
.netSource = paste.data
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void FileSelectScreen::input_open_infinipaint_file_callback(const CustomEvents::OpenInfiniPaintFileEvent& openFile) {
|
||||
main.create_new_tab(openFile);
|
||||
main.set_screen([&] (std::unique_ptr<Screen>) { return std::make_unique<PhoneDrawingProgramScreen>(main); });
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ class FileSelectScreen : public Screen {
|
|||
FileSelectScreen(MainProgram& m);
|
||||
virtual void gui_layout_run() override;
|
||||
virtual void draw(SkCanvas* canvas) override;
|
||||
virtual void input_paste_callback(const CustomEvents::PasteEvent& paste) override;
|
||||
virtual void input_open_infinipaint_file_callback(const CustomEvents::OpenInfiniPaintFileEvent& openFile) override;
|
||||
virtual void input_global_back_button_callback() override;
|
||||
virtual void input_app_about_to_go_to_background_callback() override;
|
||||
|
|
@ -65,6 +66,9 @@ class FileSelectScreen : public Screen {
|
|||
|
||||
void save_files();
|
||||
|
||||
bool connectOnPaste = false;
|
||||
std::string connectLobbyStr;
|
||||
|
||||
struct FileInfo {
|
||||
std::string fileName;
|
||||
SDL_Time lastModifyTime;
|
||||
|
|
@ -85,6 +89,7 @@ class FileSelectScreen : public Screen {
|
|||
void main_display();
|
||||
void main_menu();
|
||||
void file_view();
|
||||
void connect_view();
|
||||
void settings_view();
|
||||
void create_file_button();
|
||||
void file_view_edit();
|
||||
|
|
@ -115,6 +120,7 @@ class FileSelectScreen : public Screen {
|
|||
enum class SelectedMenu {
|
||||
FILES,
|
||||
TRASH,
|
||||
CONNECT,
|
||||
SETTINGS
|
||||
} selectedMenu = SelectedMenu::FILES;
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "../GUIStuff/ElementHelpers/TextLabelHelpers.hpp"
|
||||
#include "../GUIStuff/ElementHelpers/TextBoxHelpers.hpp"
|
||||
#include "FileSelectScreen.hpp"
|
||||
#include "../NetThreadManager.hpp"
|
||||
#include <Helpers/Logger.hpp>
|
||||
|
||||
using namespace GUIStuff;
|
||||
|
|
@ -71,9 +72,21 @@ void PhoneDrawingProgramScreen::center_message(const char* id, const std::string
|
|||
}
|
||||
|
||||
void PhoneDrawingProgramScreen::gui_layout_run() {
|
||||
gui_layout_safety_checks();
|
||||
main_display();
|
||||
}
|
||||
|
||||
void PhoneDrawingProgramScreen::gui_layout_safety_checks() {
|
||||
if(!main.world->netServer) {
|
||||
if(topToolbarSettingsPopup == TopToolbarSettingsPopup::LOBBY_INFO)
|
||||
topToolbarSettingsPopup = TopToolbarSettingsPopup::NONE;
|
||||
}
|
||||
if(!main.world->netClient && !main.world->netServer) {
|
||||
if(topToolbarSettingsPopup == TopToolbarSettingsPopup::PLAYER_LIST)
|
||||
topToolbarSettingsPopup = TopToolbarSettingsPopup::NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void PhoneDrawingProgramScreen::main_display() {
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {
|
||||
|
|
@ -83,18 +96,22 @@ void PhoneDrawingProgramScreen::main_display() {
|
|||
.layoutDirection = CLAY_TOP_TO_BOTTOM
|
||||
},
|
||||
}) {
|
||||
if(hideGUI)
|
||||
hidden_gui();
|
||||
if(main.world->netClient && main.world->clientStillConnecting)
|
||||
center_message("Connecting to server message", "Connecting to server...");
|
||||
else {
|
||||
top_toolbar();
|
||||
top_toolbar_settings_popup();
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}}
|
||||
}) {}
|
||||
bottom_toolbar();
|
||||
if(hideGUI)
|
||||
hidden_gui();
|
||||
else {
|
||||
top_toolbar();
|
||||
top_toolbar_settings_popup();
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}}
|
||||
}) {}
|
||||
bottom_toolbar();
|
||||
}
|
||||
if(!main.world->drawProg.layerMan.is_a_layer_being_edited())
|
||||
center_message("Select layer to edit message", "Select a layer to edit");
|
||||
}
|
||||
if(!main.world->drawProg.layerMan.is_a_layer_being_edited())
|
||||
center_message("Select layer to edit message", "Select a layer to edit");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -241,6 +258,97 @@ void PhoneDrawingProgramScreen::top_toolbar_settings_popup() {
|
|||
});
|
||||
break;
|
||||
}
|
||||
case TopToolbarSettingsPopup::LOBBY_INFO: {
|
||||
gui.set_z_index(gui.get_z_index() + 20, [&] {
|
||||
gui.element<LayoutElement>("host lobby info menu", [&] (LayoutElement*, const Clay_ElementId& lId) {
|
||||
Vector2f popupSize = {
|
||||
std::clamp(gui.io.safeWindowRect.width() - 10.0f, 20.0f, 300.0f),
|
||||
std::clamp(gui.io.safeWindowRect.height() - 40.0f, 50.0f, 600.0f)
|
||||
};
|
||||
CLAY(lId, {
|
||||
.layout = {
|
||||
.sizing = {.width = CLAY_SIZING_FIT(popupSize.x()), .height = CLAY_SIZING_FIT(0, popupSize.y()) },
|
||||
.padding = CLAY_PADDING_ALL(io.theme->padding1),
|
||||
.childGap = io.theme->childGap1,
|
||||
.childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_TOP},
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM
|
||||
},
|
||||
.backgroundColor = convert_vec4<Clay_Color>(io.theme->backColor1),
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(io.theme->windowCorners1),
|
||||
.floating = {.offset = {.x = 0, .y = static_cast<float>(io.theme->padding1)}, .zIndex = gui.get_z_index(), .attachPoints = {.element = CLAY_ATTACH_POINT_RIGHT_TOP, .parent = CLAY_ATTACH_POINT_RIGHT_BOTTOM}, .attachTo = CLAY_ATTACH_TO_PARENT}
|
||||
}) {
|
||||
text_label_centered(gui, "Lobby Info");
|
||||
input_text_field(gui, "lobby", "Lobby", &serverToConnectTo, {
|
||||
.immutable = true
|
||||
});
|
||||
text_button(gui, "Copy button", "Copy", {
|
||||
.wide = true,
|
||||
.onClick = [&] {
|
||||
main.input.set_clipboard_str(serverToConnectTo);
|
||||
topToolbarSettingsPopup = TopToolbarSettingsPopup::NONE;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
case TopToolbarSettingsPopup::PLAYER_LIST: {
|
||||
gui.set_z_index(gui.get_z_index() + 20, [&] {
|
||||
gui.element<LayoutElement>("player list menu", [&] (LayoutElement*, const Clay_ElementId& lId) {
|
||||
Vector2f popupSize = {
|
||||
std::clamp(gui.io.safeWindowRect.width() - 10.0f, 20.0f, 300.0f),
|
||||
std::clamp(gui.io.safeWindowRect.height() - 40.0f, 50.0f, 600.0f)
|
||||
};
|
||||
CLAY(lId, {
|
||||
.layout = {
|
||||
.sizing = {.width = CLAY_SIZING_FIT(popupSize.x()), .height = CLAY_SIZING_FIT(0, popupSize.y()) },
|
||||
.padding = CLAY_PADDING_ALL(io.theme->padding1),
|
||||
.childGap = io.theme->childGap1,
|
||||
.childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_TOP},
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM
|
||||
},
|
||||
.backgroundColor = convert_vec4<Clay_Color>(io.theme->backColor1),
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(io.theme->windowCorners1),
|
||||
.floating = {.offset = {.x = 0, .y = static_cast<float>(io.theme->padding1)}, .zIndex = gui.get_z_index(), .attachPoints = {.element = CLAY_ATTACH_POINT_RIGHT_TOP, .parent = CLAY_ATTACH_POINT_RIGHT_BOTTOM}, .attachTo = CLAY_ATTACH_TO_PARENT}
|
||||
}) {
|
||||
text_label_centered(gui, "Player List");
|
||||
left_to_right_line_layout(gui, [&]() {
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {
|
||||
.sizing = {.width = CLAY_SIZING_FIXED(20), .height = CLAY_SIZING_FIXED(20)}
|
||||
},
|
||||
.backgroundColor = convert_vec4<Clay_Color>(SkColor4f{main.world->ownClientData->get_cursor_color().x(), main.world->ownClientData->get_cursor_color().y(), main.world->ownClientData->get_cursor_color().z(), 1.0f}),
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(3)
|
||||
}) {}
|
||||
ellipse_wide_paragraph_label(gui, "own player name", main.world->ownClientData->get_display_name());
|
||||
});
|
||||
size_t num = 0;
|
||||
for(auto& client : main.world->clients->get_data()) {
|
||||
if(client != main.world->ownClientData) {
|
||||
gui.new_id(num++, [&] {
|
||||
left_to_right_line_layout(gui, [&]() {
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {
|
||||
.sizing = {.width = CLAY_SIZING_FIXED(20), .height = CLAY_SIZING_FIXED(20)}
|
||||
},
|
||||
.backgroundColor = convert_vec4<Clay_Color>(SkColor4f{client->get_cursor_color().x(), client->get_cursor_color().y(), client->get_cursor_color().z(), 1.0f}),
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(3)
|
||||
}) {}
|
||||
ellipse_wide_paragraph_label(gui, "other player name", client->get_display_name());
|
||||
text_button(gui, "teleport button", "Jump To", { .onClick = [&] {
|
||||
main.world->drawData.cam.smooth_move_to(*main.world, client->get_cam_coords(), client->get_window_size());
|
||||
topToolbarSettingsPopup = TopToolbarSettingsPopup::NONE;
|
||||
}});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -268,9 +376,8 @@ void PhoneDrawingProgramScreen::top_toolbar() {
|
|||
svg_icon_button(gui, "back exit button", "data/icons/RemixIcon/arrow-left-s-line.svg", {
|
||||
.drawType = SelectableButton::DrawType::TRANSPARENT_ALL,
|
||||
.onClick = [&] {
|
||||
main.world->save_to_file(main.world->filePath);
|
||||
save_to_file();
|
||||
main.set_tab_to_close(main.world.get());
|
||||
main.set_screen([&] (std::unique_ptr<Screen>) { return std::make_unique<FileSelectScreen>(main); });
|
||||
}
|
||||
});
|
||||
top_toolbar_remaining_area();
|
||||
|
|
@ -350,17 +457,24 @@ void PhoneDrawingProgramScreen::top_toolbar_remaining_area() {
|
|||
break;
|
||||
spaceForWorldName -= BUTTON_WIDTH;
|
||||
}
|
||||
input_text(gui, "world name text", &main.world->name, {
|
||||
.decorations = false,
|
||||
.onSelect = [&] {
|
||||
fileOldPath = main.world->filePath;
|
||||
},
|
||||
.onDeselect = [&] {
|
||||
std::filesystem::remove(fileOldPath);
|
||||
std::filesystem::remove(fileOldPath.parent_path() / (fileOldPath.stem().string() + ".jpg"));
|
||||
main.world->autosave_to_directory(main.world->filePath.parent_path());
|
||||
}
|
||||
});
|
||||
if(main.world->netClient) {
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {.sizing = {.width = CLAY_SIZING_GROW(0)}}
|
||||
}) { }
|
||||
}
|
||||
else {
|
||||
input_text(gui, "world name text", &main.world->name, {
|
||||
.decorations = false,
|
||||
.onSelect = [&] {
|
||||
fileOldPath = main.world->filePath;
|
||||
},
|
||||
.onDeselect = [&] {
|
||||
std::filesystem::remove(fileOldPath);
|
||||
std::filesystem::remove(fileOldPath.parent_path() / (fileOldPath.stem().string() + ".jpg"));
|
||||
main.world->autosave_to_directory(main.world->filePath.parent_path());
|
||||
}
|
||||
});
|
||||
}
|
||||
gui.new_id("visible buttons", [&] {
|
||||
for(size_t i = 0; i < buttonFreeSpace; i++) {
|
||||
gui.new_id(i, [&] {
|
||||
|
|
@ -418,15 +532,37 @@ void PhoneDrawingProgramScreen::top_toolbar_hidden_button_popup(GUIStuff::Elemen
|
|||
}
|
||||
});
|
||||
|
||||
l.emplace_back(TopToolbarRemainingAreaButton{
|
||||
.name = "Host",
|
||||
.svgPath = "data/icons/network.svg",
|
||||
.onClick = [&] {
|
||||
serverLocalID = NetLibrary::get_random_server_local_id();
|
||||
serverToConnectTo = NetLibrary::get_global_id() + serverLocalID;
|
||||
topToolbarSettingsPopup = topToolbarSettingsPopup == TopToolbarSettingsPopup::HOST ? TopToolbarSettingsPopup::NONE : TopToolbarSettingsPopup::HOST;
|
||||
}
|
||||
});
|
||||
if(!main.world->netClient && !main.world->netServer) {
|
||||
l.emplace_back(TopToolbarRemainingAreaButton{
|
||||
.name = "Host",
|
||||
.svgPath = "data/icons/network.svg",
|
||||
.onClick = [&] {
|
||||
serverLocalID = NetLibrary::get_random_server_local_id();
|
||||
serverToConnectTo = NetLibrary::get_global_id() + serverLocalID;
|
||||
topToolbarSettingsPopup = topToolbarSettingsPopup == TopToolbarSettingsPopup::HOST ? TopToolbarSettingsPopup::NONE : TopToolbarSettingsPopup::HOST;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if(main.world->netServer) {
|
||||
l.emplace_back(TopToolbarRemainingAreaButton{
|
||||
.name = "Lobby Info",
|
||||
.svgPath = "data/icons/network.svg",
|
||||
.onClick = [&] {
|
||||
topToolbarSettingsPopup = topToolbarSettingsPopup == TopToolbarSettingsPopup::LOBBY_INFO ? TopToolbarSettingsPopup::NONE : TopToolbarSettingsPopup::LOBBY_INFO;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if(main.world->netClient || main.world->netServer) {
|
||||
l.emplace_back(TopToolbarRemainingAreaButton{
|
||||
.name = "Player List",
|
||||
.svgPath = "data/icons/list.svg",
|
||||
.onClick = [&] {
|
||||
topToolbarSettingsPopup = topToolbarSettingsPopup == TopToolbarSettingsPopup::PLAYER_LIST ? TopToolbarSettingsPopup::NONE : TopToolbarSettingsPopup::PLAYER_LIST;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
gui.set_z_index(gui.get_z_index() + 20, [&] {
|
||||
dropdown_many_element_popup_layout(gui, "top toolbar hidden button popup", {
|
||||
|
|
@ -1017,15 +1153,23 @@ void PhoneDrawingProgramScreen::input_global_back_button_callback() {
|
|||
main.g.gui.set_to_layout();
|
||||
}
|
||||
else {
|
||||
main.world->save_to_file(main.world->filePath);
|
||||
save_to_file();
|
||||
main.set_tab_to_close(main.world.get());
|
||||
main.g.gui.set_to_layout();
|
||||
main.set_screen([&] (std::unique_ptr<Screen>) { return std::make_unique<FileSelectScreen>(main); });
|
||||
}
|
||||
}
|
||||
|
||||
void PhoneDrawingProgramScreen::save_to_file() {
|
||||
if(!main.world->netClient)
|
||||
main.world->save_to_file(main.world->filePath);
|
||||
}
|
||||
|
||||
void PhoneDrawingProgramScreen::input_app_about_to_go_to_background_callback() {
|
||||
main.world->save_to_file(main.world->filePath);
|
||||
save_to_file();
|
||||
}
|
||||
|
||||
void PhoneDrawingProgramScreen::on_tab_close() {
|
||||
main.set_screen([&] (std::unique_ptr<Screen>) { return std::make_unique<FileSelectScreen>(main); });
|
||||
}
|
||||
|
||||
void PhoneDrawingProgramScreen::color_selector(Vector4f* color, const ColorSelectorData& colorSelectorData) {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ class PhoneDrawingProgramScreen : public DrawingProgramScreen {
|
|||
virtual void gui_layout_run() override;
|
||||
virtual void input_global_back_button_callback() override;
|
||||
virtual void input_app_about_to_go_to_background_callback() override;
|
||||
virtual void on_tab_close() override;
|
||||
struct ColorSelectorData {
|
||||
std::function<void()> onChange;
|
||||
std::function<void()> onSelect;
|
||||
|
|
@ -73,7 +74,9 @@ class PhoneDrawingProgramScreen : public DrawingProgramScreen {
|
|||
BOOKMARKS,
|
||||
LAYERS,
|
||||
GRIDS,
|
||||
HOST
|
||||
HOST,
|
||||
LOBBY_INFO,
|
||||
PLAYER_LIST
|
||||
} topToolbarSettingsPopup = TopToolbarSettingsPopup::NONE;
|
||||
|
||||
bool hideGUI = false;
|
||||
|
|
@ -82,6 +85,7 @@ class PhoneDrawingProgramScreen : public DrawingProgramScreen {
|
|||
Vector4f* colorPickerPtr = nullptr;
|
||||
ColorSelectorData colorPickerData;
|
||||
|
||||
void gui_layout_safety_checks();
|
||||
void hidden_gui();
|
||||
void center_message(const char* id, const std::string& m);
|
||||
void default_bottom_toolbar();
|
||||
|
|
@ -95,6 +99,7 @@ class PhoneDrawingProgramScreen : public DrawingProgramScreen {
|
|||
void bottom_toolbar();
|
||||
void main_display();
|
||||
void tool_settings_popup();
|
||||
void save_to_file();
|
||||
|
||||
std::string serverToConnectTo;
|
||||
std::string serverLocalID;
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ void Screen::input_finger_motion_callback(const InputManager::FingerMotionCallba
|
|||
void Screen::input_window_resize_callback(const InputManager::WindowResizeCallbackArgs& w) {}
|
||||
void Screen::input_window_scale_callback(const InputManager::WindowScaleCallbackArgs& w) {}
|
||||
void Screen::input_app_about_to_go_to_background_callback() {}
|
||||
void Screen::input_app_about_to_go_to_foreground_callback() {}
|
||||
void Screen::on_tab_close() {}
|
||||
std::optional<InputManager::TextBoxStartInfo> Screen::get_text_box_start_info() { return std::nullopt; }
|
||||
|
||||
Screen::~Screen() {}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ class Screen {
|
|||
virtual void input_window_resize_callback(const InputManager::WindowResizeCallbackArgs& w);
|
||||
virtual void input_window_scale_callback(const InputManager::WindowScaleCallbackArgs& w);
|
||||
virtual void input_app_about_to_go_to_background_callback();
|
||||
virtual void input_app_about_to_go_to_foreground_callback();
|
||||
virtual void on_tab_close();
|
||||
virtual std::optional<InputManager::TextBoxStartInfo> get_text_box_start_info();
|
||||
|
||||
virtual void open_file_selector(const std::string& filePickerName, const std::vector<ExtensionFilter>& extensionFilters, OpenFileSelectorCallback postSelectionFunc, const std::string& fileName = "", bool isSaving = false);
|
||||
|
|
|
|||
|
|
@ -1601,8 +1601,11 @@ void Toolbar::general_settings_inner_gui() {
|
|||
switch(generalSettingsOptions) {
|
||||
case GSETTINGS_GENERAL: {
|
||||
general_scroll_area("general settings", [&] {
|
||||
input_text_field(gui, "display name input", "Display name", &main.conf.displayName);
|
||||
main.update_display_names();
|
||||
input_text_field(gui, "display name input", "Display name", &main.conf.displayName, {
|
||||
.onEdit = [&] {
|
||||
main.update_display_names();
|
||||
}
|
||||
});
|
||||
color_picker_button_field(gui, "defaultCanvasBackgroundColor", "Default canvas background color", &main.conf.defaultCanvasBackgroundColor, { .hasAlpha = false });
|
||||
#ifndef __EMSCRIPTEN__
|
||||
checkbox_boolean_field(gui, "native file pick", "Use native file picker", &main.conf.useNativeFilePicker);
|
||||
|
|
|
|||
|
|
@ -227,6 +227,7 @@ bool World::connection_update() {
|
|||
Logger::get().log("USERINFO", "Client connection failed");
|
||||
netObjMan.disconnect();
|
||||
main.set_tab_to_close(this);
|
||||
netClient = nullptr;
|
||||
return false;
|
||||
}
|
||||
netClient->update();
|
||||
|
|
|
|||
|
|
@ -670,6 +670,10 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
|
|||
Logger::get().log("INFO", "[SDL_AppEvent] Entering background");
|
||||
mS.m->input_app_about_to_go_to_background_callback();
|
||||
break;
|
||||
case SDL_EVENT_WILL_ENTER_FOREGROUND:
|
||||
Logger::get().log("INFO", "[SDL_AppEvent] Entering foreground");
|
||||
mS.m->input_app_about_to_go_to_foreground_callback();
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||
if(event->window.windowID == SDL_GetWindowID(mS.window)) {
|
||||
if(mS.m->app_close_requested())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue