diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0eb8b14..a70e478 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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"
diff --git a/android-project/app/build.gradle b/android-project/app/build.gradle
index 769a7a2..e9adba0 100644
--- a/android-project/app/build.gradle
+++ b/android-project/app/build.gradle
@@ -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"
diff --git a/android-project/app/proguard-rules.pro b/android-project/app/proguard-rules.pro
index 3156ca7..6fac289 100644
--- a/android-project/app/proguard-rules.pro
+++ b/android-project/app/proguard-rules.pro
@@ -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 {
diff --git a/android-project/app/src/main/AndroidManifest.xml b/android-project/app/src/main/AndroidManifest.xml
index b8111a8..7f52343 100644
--- a/android-project/app/src/main/AndroidManifest.xml
+++ b/android-project/app/src/main/AndroidManifest.xml
@@ -58,6 +58,9 @@
+
+
+
+
+
+
(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 server) {
void NetLibrary::register_client(std::shared_ptr 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();
- //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
}
diff --git a/include/Helpers/Networking/NetLibrary.hpp b/include/Helpers/Networking/NetLibrary.hpp
index 1dc56f6..2d4f586 100644
--- a/include/Helpers/Networking/NetLibrary.hpp
+++ b/include/Helpers/Networking/NetLibrary.hpp
@@ -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 server);
static void register_client(std::shared_ptr 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;
diff --git a/src/AndroidJNICalls.cpp b/src/AndroidJNICalls.cpp
index 175c6b2..f08bffe 100644
--- a/src/AndroidJNICalls.cpp
+++ b/src/AndroidJNICalls.cpp
@@ -25,6 +25,7 @@
#include
#include
#include
+#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(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(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& 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
\ No newline at end of file
diff --git a/src/AndroidJNICalls.hpp b/src/AndroidJNICalls.hpp
index 4cf6fab..e947866 100644
--- a/src/AndroidJNICalls.hpp
+++ b/src/AndroidJNICalls.hpp
@@ -27,6 +27,8 @@ namespace AndroidJNICalls {
extern InputManager* globalInputManager;
void startTextInput(CustomEvents::InputTextBoxID newTextboxID, const std::shared_ptr& newTextbox, const std::shared_ptr& newCursor, const std::shared_ptr& modMap, int inputType);
+ void startNetworkService();
+ void stopNetworkService();
void updateCursorPos(CustomEvents::InputTextBoxID tId, const std::shared_ptr& newCursor);
void updateModMap(CustomEvents::InputTextBoxID tId, const std::shared_ptr& newModMap);
void updateTextboxAndCursorPos(CustomEvents::InputTextBoxID tId, const std::shared_ptr& newTextbox, const std::shared_ptr& newCursor);
diff --git a/src/DrawingProgram/Tools/GridModifyTool.cpp b/src/DrawingProgram/Tools/GridModifyTool.cpp
index 67f05f5..82b5119 100644
--- a/src/DrawingProgram/Tools/GridModifyTool.cpp
+++ b/src/DrawingProgram/Tools/GridModifyTool.cpp
@@ -23,7 +23,7 @@
#include "Helpers/NetworkingObjects/NetObjTemporaryPtr.decl.hpp"
#include
#include
-
+#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 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(g.bounds.has_value());
checkbox_boolean_field(gui, "Bounded", "Bounded", bounded.get(), [&, bounded] {
if(*bounded) {
diff --git a/src/DrawingProgram/Tools/GridModifyTool.hpp b/src/DrawingProgram/Tools/GridModifyTool.hpp
index c8e7332..4e4167f 100644
--- a/src/DrawingProgram/Tools/GridModifyTool.hpp
+++ b/src/DrawingProgram/Tools/GridModifyTool.hpp
@@ -28,6 +28,7 @@ class GridModifyTool : public DrawingProgramToolBase {
GridModifyTool(DrawingProgram& initDrawP);
void set_grid(const NetworkingObjects::NetObjWeakPtr& 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;
diff --git a/src/MainProgram.cpp b/src/MainProgram.cpp
index f3890a4..e4dfde3 100755
--- a/src/MainProgram.cpp
+++ b/src/MainProgram.cpp
@@ -30,6 +30,7 @@
#include
#include
#include "InputManager.hpp"
+#include "NetThreadManager.hpp"
#include
#include
#include
@@ -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();
}
diff --git a/src/MainProgram.hpp b/src/MainProgram.hpp
index f50d8f2..7f9cf77 100644
--- a/src/MainProgram.hpp
+++ b/src/MainProgram.hpp
@@ -111,6 +111,7 @@ class MainProgram {
MainProgram();
void update();
+ void background_update();
void draw_world(SkCanvas* canvas, std::shared_ptr worldToDraw, const DrawData& drawData);
void draw(SkCanvas* canvas);
sk_sp 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);
diff --git a/src/NetThreadManager.cpp b/src/NetThreadManager.cpp
new file mode 100644
index 0000000..8575e54
--- /dev/null
+++ b/src/NetThreadManager.cpp
@@ -0,0 +1,79 @@
+#include "NetThreadManager.hpp"
+#include
+#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([&]{ 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
+}
diff --git a/src/NetThreadManager.hpp b/src/NetThreadManager.hpp
new file mode 100644
index 0000000..fa5edd9
--- /dev/null
+++ b/src/NetThreadManager.hpp
@@ -0,0 +1,27 @@
+#pragma once
+#include
+#include
+#include
+
+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 t;
+ std::atomic destroyThread = false;
+
+ bool started = false;
+};
diff --git a/src/Screens/DesktopDrawingProgramScreen.cpp b/src/Screens/DesktopDrawingProgramScreen.cpp
index 0547677..4bc4120 100644
--- a/src/Screens/DesktopDrawingProgramScreen.cpp
+++ b/src/Screens/DesktopDrawingProgramScreen.cpp
@@ -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);
+}
diff --git a/src/Screens/DesktopDrawingProgramScreen.hpp b/src/Screens/DesktopDrawingProgramScreen.hpp
index d2ffc49..0658f82 100644
--- a/src/Screens/DesktopDrawingProgramScreen.hpp
+++ b/src/Screens/DesktopDrawingProgramScreen.hpp
@@ -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& extensionFilters, OpenFileSelectorCallback postSelectionFunc, const std::string& fileName = "", bool isSaving = false) override;
+ virtual void on_tab_close() override;
private:
Toolbar toolbar;
diff --git a/src/Screens/FileSelectScreen.cpp b/src/Screens/FileSelectScreen.cpp
index be107f1..58e9848 100644
--- a/src/Screens/FileSelectScreen.cpp
+++ b/src/Screens/FileSelectScreen.cpp
@@ -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("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({
+ .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({
+ .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) { return std::make_unique(main); });
diff --git a/src/Screens/FileSelectScreen.hpp b/src/Screens/FileSelectScreen.hpp
index 2f0c4c9..634bf05 100644
--- a/src/Screens/FileSelectScreen.hpp
+++ b/src/Screens/FileSelectScreen.hpp
@@ -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;
diff --git a/src/Screens/PhoneDrawingProgramScreen.cpp b/src/Screens/PhoneDrawingProgramScreen.cpp
index 1285750..353367e 100644
--- a/src/Screens/PhoneDrawingProgramScreen.cpp
+++ b/src/Screens/PhoneDrawingProgramScreen.cpp
@@ -29,6 +29,7 @@
#include "../GUIStuff/ElementHelpers/TextLabelHelpers.hpp"
#include "../GUIStuff/ElementHelpers/TextBoxHelpers.hpp"
#include "FileSelectScreen.hpp"
+#include "../NetThreadManager.hpp"
#include
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("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(io.theme->backColor1),
+ .cornerRadius = CLAY_CORNER_RADIUS(io.theme->windowCorners1),
+ .floating = {.offset = {.x = 0, .y = static_cast(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("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(io.theme->backColor1),
+ .cornerRadius = CLAY_CORNER_RADIUS(io.theme->windowCorners1),
+ .floating = {.offset = {.x = 0, .y = static_cast(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(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(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) { return std::make_unique(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) { return std::make_unique(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) { return std::make_unique(main); });
}
void PhoneDrawingProgramScreen::color_selector(Vector4f* color, const ColorSelectorData& colorSelectorData) {
diff --git a/src/Screens/PhoneDrawingProgramScreen.hpp b/src/Screens/PhoneDrawingProgramScreen.hpp
index 5d2e908..5385e66 100644
--- a/src/Screens/PhoneDrawingProgramScreen.hpp
+++ b/src/Screens/PhoneDrawingProgramScreen.hpp
@@ -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 onChange;
std::function 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;
diff --git a/src/Screens/Screen.cpp b/src/Screens/Screen.cpp
index f81a540..97cb3ee 100644
--- a/src/Screens/Screen.cpp
+++ b/src/Screens/Screen.cpp
@@ -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 Screen::get_text_box_start_info() { return std::nullopt; }
Screen::~Screen() {}
diff --git a/src/Screens/Screen.hpp b/src/Screens/Screen.hpp
index 156a234..e391053 100644
--- a/src/Screens/Screen.hpp
+++ b/src/Screens/Screen.hpp
@@ -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 get_text_box_start_info();
virtual void open_file_selector(const std::string& filePickerName, const std::vector& extensionFilters, OpenFileSelectorCallback postSelectionFunc, const std::string& fileName = "", bool isSaving = false);
diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp
index acf4807..7225c3d 100644
--- a/src/Toolbar.cpp
+++ b/src/Toolbar.cpp
@@ -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);
diff --git a/src/World.cpp b/src/World.cpp
index 08fa560..344fe7e 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -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();
diff --git a/src/main.cpp b/src/main.cpp
index 2fe65f6..a429271 100755
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -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())