diff --git a/PanoPainter.vcxproj b/PanoPainter.vcxproj
index 667d395..85f4df0 100644
--- a/PanoPainter.vcxproj
+++ b/PanoPainter.vcxproj
@@ -352,6 +352,7 @@
+
@@ -478,6 +479,7 @@
+
diff --git a/PanoPainter.vcxproj.filters b/PanoPainter.vcxproj.filters
index 5e0ede5..d359561 100644
--- a/PanoPainter.vcxproj.filters
+++ b/PanoPainter.vcxproj.filters
@@ -366,6 +366,9 @@
libs\glad
+
+ Source Files
+
@@ -608,6 +611,9 @@
Header Files\ui
+
+ Header Files
+
diff --git a/PanoPainter.xcodeproj/project.pbxproj b/PanoPainter.xcodeproj/project.pbxproj
index deedb4e..fa5c59f 100644
--- a/PanoPainter.xcodeproj/project.pbxproj
+++ b/PanoPainter.xcodeproj/project.pbxproj
@@ -156,6 +156,8 @@
AD7FB09222470DE5005913AB /* app_vr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD7FB09122470DE2005913AB /* app_vr.cpp */; };
AD7FB09322470DE5005913AB /* app_vr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD7FB09122470DE2005913AB /* app_vr.cpp */; };
AD83A0D323268B6F005B0871 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AD83A0D223268B6F005B0871 /* MobileCoreServices.framework */; };
+ AD83A0D9232A7906005B0871 /* node_input_box.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD83A0D7232A7905005B0871 /* node_input_box.cpp */; };
+ AD83A0DA232A793A005B0871 /* node_input_box.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD83A0D7232A7905005B0871 /* node_input_box.cpp */; };
ADA2A57822BE8D8E00C6B6C9 /* node_tool_bucket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ADA2A57622BE8D8E00C6B6C9 /* node_tool_bucket.cpp */; };
ADA2A57922BE8D8E00C6B6C9 /* node_tool_bucket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ADA2A57622BE8D8E00C6B6C9 /* node_tool_bucket.cpp */; };
ADA3ABF6222C8C370083B825 /* node_panel_quick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ADA3ABF4222C8C350083B825 /* node_panel_quick.cpp */; };
@@ -462,6 +464,8 @@
AD7FB08E22470C75005913AB /* node_panel_floating.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = node_panel_floating.cpp; sourceTree = ""; };
AD7FB09122470DE2005913AB /* app_vr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = app_vr.cpp; sourceTree = ""; };
AD83A0D223268B6F005B0871 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.4.sdk/System/Library/Frameworks/MobileCoreServices.framework; sourceTree = DEVELOPER_DIR; };
+ AD83A0D7232A7905005B0871 /* node_input_box.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = node_input_box.cpp; sourceTree = ""; };
+ AD83A0D8232A7906005B0871 /* node_input_box.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = node_input_box.h; sourceTree = ""; };
AD8CF71F1E913F0500083FFD /* log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = log.cpp; sourceTree = ""; };
AD8CF7201E913F0500083FFD /* log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log.h; sourceTree = ""; };
AD95AEC31E41EDEC002DD03A /* font.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = font.cpp; sourceTree = ""; };
@@ -728,6 +732,8 @@
AD1063621EC7ADFA002A525F /* node_image_texture.h */,
AD1063631EC7ADFA002A525F /* node_image.cpp */,
AD1063641EC7ADFA002A525F /* node_image.h */,
+ AD83A0D7232A7905005B0871 /* node_input_box.cpp */,
+ AD83A0D8232A7906005B0871 /* node_input_box.h */,
AD1063651EC7ADFA002A525F /* node_message_box.cpp */,
AD1063661EC7ADFA002A525F /* node_message_box.h */,
AD1063671EC7ADFA002A525F /* node_panel_brush.cpp */,
@@ -1478,6 +1484,7 @@
AD0E5CC01ECC72AD00C35669 /* node_icon.cpp in Sources */,
AD0E5CC11ECC72AD00C35669 /* node_image_texture.cpp in Sources */,
ADF4F7762263920400422C7C /* settings.cpp in Sources */,
+ AD83A0DA232A793A005B0871 /* node_input_box.cpp in Sources */,
AD0E5CBD1ECC72AD00C35669 /* node_checkbox.cpp in Sources */,
ADBC8C581FAFD05A0094B339 /* node_dialog_cloud.cpp in Sources */,
AD7FB086223E8A43005913AB /* YGLayout.cpp in Sources */,
@@ -1512,6 +1519,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ AD83A0D9232A7906005B0871 /* node_input_box.cpp in Sources */,
AD7FB07B223E8A42005913AB /* log.cpp in Sources */,
AD74030F1FB9D0D3004ACFCC /* node_combobox.cpp in Sources */,
ADBC8C571FAFD05A0094B339 /* node_dialog_cloud.cpp in Sources */,
diff --git a/PanoPainter/GameViewController.h b/PanoPainter/GameViewController.h
index 41f69f9..5fe26ce 100644
--- a/PanoPainter/GameViewController.h
+++ b/PanoPainter/GameViewController.h
@@ -34,6 +34,7 @@
- (void)async_swap;
- (void)pick_photo:(std::function) callback;
- (void)pick_file:(NSArray*)types then:(std::function)callback;
+- (void)pick_file_save:(std::string)path;
- (void)registerForKeyboardNotifications;
- (void)unregisterForKeyboardNotifications;
- (void)crash;
diff --git a/PanoPainter/GameViewController.m b/PanoPainter/GameViewController.m
index 259641e..0efca1a 100644
--- a/PanoPainter/GameViewController.m
+++ b/PanoPainter/GameViewController.m
@@ -180,18 +180,28 @@ std::recursive_mutex lock_mutex;
(__bridge CFStringRef)ext, NULL);
[UTIs addObject:typeForExt];
}
-
+
GameFilePicker *picker = [[GameFilePicker alloc] initWithDocumentTypes:UTIs inMode:UIDocumentPickerModeImport];
picker.delegate = self;
picker->callback = callback;
[self presentViewController:picker animated:YES completion:NULL];
}
+- (void)pick_file_save:(std::string)path
+{
+ NSURL* url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:path.c_str()]];
+ GameFilePicker *picker = [[GameFilePicker alloc] initWithURL:url inMode:UIDocumentPickerModeMoveToService];
+ picker.delegate = self;
+ [self presentViewController:picker animated:YES completion:NULL];
+}
-(void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url
{
NSString *chosenImage = [url path];
GameFilePicker* p = static_cast(controller);
- std::string path = [chosenImage cStringUsingEncoding:NSUTF8StringEncoding];
- p->callback(path);
+ if (p->callback)
+ {
+ std::string path = [chosenImage cStringUsingEncoding:NSUTF8StringEncoding];
+ p->callback(path);
+ }
[controller dismissViewControllerAnimated:YES completion:NULL];
}
-(void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller
diff --git a/data/layout.xml b/data/layout.xml
index 869673c..a827bdf 100644
--- a/data/layout.xml
+++ b/data/layout.xml
@@ -873,7 +873,28 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app.h b/src/app.h
index 03a27f8..4331397 100644
--- a/src/app.h
+++ b/src/app.h
@@ -33,6 +33,7 @@
#endif
#include "node_panel_grid.h"
#include "node_panel_quick.h"
+#include "node_input_box.h"
struct VRController
{
@@ -159,7 +160,11 @@ public:
bool clipboard_set_text(const std::string& s);
void pick_image(std::function callback);
void pick_file(std::vector types, std::function callback);
+#if __IOS__
+ void pick_file_save(const std::string& type, std::function writer);
+#else
void pick_file_save(std::vector types, std::function callback);
+#endif
void pick_dir(std::function callback);
void display_file(std::string path);
void share_file(std::string path);
@@ -206,7 +211,6 @@ public:
bool key_char(char key);
void toggle_ui();
void set_stylus();
- std::shared_ptr message_box(const std::string& title, const std::string& text, bool cancel_button = false);
void rec_clear();
void rec_loop();
@@ -241,11 +245,17 @@ public:
void cloud_upload();
void cloud_upload_all();
void cloud_browse();
- void upload(std::string filename, std::string name = "", std::function progress = nullptr);
- void download(std::string url, std::string dest_filepath, std::function progress = nullptr);
+ void upload(std::string filename, std::string name = "",
+ std::function progress = nullptr);
+ void download(std::string url, std::string dest_filepath,
+ std::function progress = nullptr);
bool check_license();
std::shared_ptr show_progress(const std::string& title, int total = 0);
+ std::shared_ptr message_box(const std::string& title,
+ const std::string& text, bool cancel_button = false);
+ std::shared_ptr input_box(const std::string& title,
+ const std::string& field_name, const std::string& ok_caption = "Ok");
void brush_update(bool update_color, bool update_brush);
void title_update();
diff --git a/src/app_dialogs.cpp b/src/app_dialogs.cpp
index 320d11c..f611497 100644
--- a/src/app_dialogs.cpp
+++ b/src/app_dialogs.cpp
@@ -44,6 +44,21 @@ std::shared_ptr App::message_box(const std::string &title, const
return m;
}
+std::shared_ptr App::input_box(const std::string& title,
+ const std::string& field_name, const std::string& ok_caption /*= "Ok"*/)
+{
+ auto m = std::make_shared();
+ m->m_manager = &layout;
+ m->init();
+ m->create();
+ m->loaded();
+ m->m_title->set_text(title.c_str());
+ m->m_field_name->set_text(field_name.c_str());
+ m->btn_ok->m_text->set_text(ok_caption.c_str());
+ layout[main_id]->add_child(m);
+ return m;
+}
+
void App::dialog_usermanual()
{
auto dialog = std::make_shared();
diff --git a/src/app_events.cpp b/src/app_events.cpp
index 1293b29..5a97174 100644
--- a/src/app_events.cpp
+++ b/src/app_events.cpp
@@ -192,14 +192,32 @@ void App::pick_file(std::vector types, std::function writer)
+{
+ redraw = true;
+ std::thread([=]{
+ auto mb = input_box("Insert", "File name");
+ std::string placeholder = "name." + type;
+ mb->m_field_text->set_text(placeholder.c_str());
+ mb->on_submit = [this, writer, type] (Node* target, std::string name) {
+ std::string ext = "." + type;
+ std::string path = data_path + "/" + name;
+ if (name.find(ext) == std::string::npos)
+ path += ext;
+ writer(path);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [ios_view pick_file_save:path];
+ });
+ target->destroy();
+ };
+ }).detach();
+}
+#else
void App::pick_file_save(std::vector types, std::function callback)
{
redraw = true;
-#ifdef __IOS__
-// dispatch_async(dispatch_get_main_queue(), ^{
-// [ios_view pick_file_save:callback];
-// });
-#elif __OSX__
+#if __OSX__
dispatch_async(dispatch_get_main_queue(), ^{
//NSArray* fileTypes = [NSArray arrayWithObjects:@"ppi", @"PPI", nil];
NSMutableArray* fileTypes = [NSMutableArray arrayWithCapacity:types.size()];
@@ -233,6 +251,7 @@ void App::pick_file_save(std::vector types, std::function callback)
{
diff --git a/src/node_input_box.cpp b/src/node_input_box.cpp
new file mode 100644
index 0000000..347b891
--- /dev/null
+++ b/src/node_input_box.cpp
@@ -0,0 +1,57 @@
+#include "pch.h"
+#include "log.h"
+#include "node_input_box.h"
+#include "layout.h"
+
+Node* NodeInputBox::clone_instantiate() const
+{
+ return new NodeInputBox();
+}
+
+void NodeInputBox::init()
+{
+ SetPosition(0, 0);
+ SetWidthP(100);
+ SetHeightP(100);
+ SetPositioning(YGPositionTypeAbsolute);
+ auto m_template = (*m_manager)[const_hash("input-box")]->m_children[0]->clone();
+ add_child(m_template);
+ m_title = m_template->find("title");
+ m_field_name = m_template->find("field-name");
+ m_field_text = m_template->find("field-text");
+ btn_ok = m_template->find("btn-ok");
+ btn_ok->on_click = [&](Node*) {
+ if (on_submit)
+ on_submit(this, m_field_text->m_text->m_text);
+ };
+ btn_cancel = m_template->find("btn-cancel");
+ btn_cancel->on_click = [&](Node*) { destroy(); };
+ m_capture_children = false; // don't capture children events on mouse_capture
+}
+
+kEventResult NodeInputBox::handle_event(Event* e)
+{
+ Node::handle_event(e);
+ auto ke = (KeyEvent*)e;
+ switch (e->m_type)
+ {
+ case kEventType::KeyDown:
+ break;
+ case kEventType::KeyUp:
+ if (ke->m_key == kKey::KeyEnter && on_submit)
+ on_submit(this, m_field_text->m_text->m_text);
+ break;
+ case kEventType::KeyChar:
+ break;
+ default:
+ return kEventResult::Available;
+ break;
+ }
+ return kEventResult::Consumed;
+}
+
+void NodeInputBox::added(Node* parent)
+{
+ Node::added(parent);
+ mouse_capture();
+}
diff --git a/src/node_input_box.h b/src/node_input_box.h
new file mode 100644
index 0000000..4511239
--- /dev/null
+++ b/src/node_input_box.h
@@ -0,0 +1,19 @@
+#pragma once
+#include "node.h"
+#include "node_button.h"
+#include "node_text_input.h"
+
+class NodeInputBox : public Node
+{
+public:
+ std::function on_submit = nullptr;
+ NodeButton* btn_ok;
+ NodeButton* btn_cancel;
+ NodeText* m_field_name;
+ NodeTextInput* m_field_text;
+ NodeText* m_title;
+ virtual Node* clone_instantiate() const override;
+ virtual void init() override;
+ virtual kEventResult handle_event(Event* e) override;
+ virtual void added(Node* parent) override;
+};
diff --git a/src/node_panel_brush.cpp b/src/node_panel_brush.cpp
index f72ffac..98e485e 100644
--- a/src/node_panel_brush.cpp
+++ b/src/node_panel_brush.cpp
@@ -502,6 +502,11 @@ void NodePanelBrushPreset::init()
});
break;
case 1: // export file
+ #if __IOS__
+ App::I->pick_file_save("ppbr", [this] (std::string path) {
+ export_ppbr(path, {});
+ });
+ #else
App::I->pick_file_save({ "ppbr" }, [this] (std::string path) {
std::thread([this, path] {
BT_SetTerminate();
@@ -509,6 +514,7 @@ void NodePanelBrushPreset::init()
App::I->message_box("Export PPBR", "Brushes exported to:\n" + path);
}).detach();
});
+ #endif
break;
case 2: // download
break;