add remote page loading

This commit is contained in:
2019-11-27 19:55:31 +01:00
parent 7701e6771b
commit 41579fa3c6
18 changed files with 373 additions and 41 deletions

View File

@@ -370,6 +370,7 @@
<ClCompile Include="src\node_image_texture.cpp" />
<ClCompile Include="src\node_input_box.cpp" />
<ClCompile Include="src\node_message_box.cpp" />
<ClCompile Include="src\node_metadata.cpp" />
<ClCompile Include="src\node_panel_brush.cpp" />
<ClCompile Include="src\node_panel_color.cpp" />
<ClCompile Include="src\node_panel_floating.cpp" />
@@ -380,6 +381,7 @@
<ClCompile Include="src\node_panel_animation.cpp" />
<ClCompile Include="src\node_popup_menu.cpp" />
<ClCompile Include="src\node_progress_bar.cpp" />
<ClCompile Include="src\node_remote_page.cpp" />
<ClCompile Include="src\node_scroll.cpp" />
<ClCompile Include="src\node_settings.cpp" />
<ClCompile Include="src\node_slider.cpp" />
@@ -500,6 +502,7 @@
<ClInclude Include="src\node_image_texture.h" />
<ClInclude Include="src\node_input_box.h" />
<ClInclude Include="src\node_message_box.h" />
<ClInclude Include="src\node_metadata.h" />
<ClInclude Include="src\node_panel_brush.h" />
<ClInclude Include="src\node_panel_color.h" />
<ClInclude Include="src\node_panel_floating.h" />
@@ -510,6 +513,7 @@
<ClInclude Include="src\node_panel_animation.h" />
<ClInclude Include="src\node_popup_menu.h" />
<ClInclude Include="src\node_progress_bar.h" />
<ClInclude Include="src\node_remote_page.h" />
<ClInclude Include="src\node_scroll.h" />
<ClInclude Include="src\node_settings.h" />
<ClInclude Include="src\node_slider.h" />

View File

@@ -384,6 +384,12 @@
<ClCompile Include="src\mp4enc.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\node_remote_page.cpp">
<Filter>Source Files\ui</Filter>
</ClCompile>
<ClCompile Include="src\node_metadata.cpp">
<Filter>Source Files\ui</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="libs\jpeg\jpgd.h">
@@ -638,6 +644,12 @@
<ClInclude Include="src\mp4enc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\node_remote_page.h">
<Filter>Source Files\ui</Filter>
</ClInclude>
<ClInclude Include="src\node_metadata.h">
<Filter>Source Files\ui</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="PanoPainter.rc">

View File

@@ -0,0 +1,28 @@
<?xml version="1.0"?>
<root
xmlns="http://panopainter.com/layout.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>
<layout id="remote-page">
<border positioning="absolute" position="0 0" color=".4 .4 .4 .8" width="100%" height="100%" align="center" justify="center" mouse-capture="1">
<border thickness="1" border-color=".2" pad="3" max-width="650" dir="col" width="80%" height="80%">
<border width="100%" height="30" color=".2 .2 .2 .9" dir="row" align="center" justify="center">
<text id="title" text="PageTitle"></text>
</border>
<border width="100%" height="100%" color="0 0 0 .9" pad="10" dir="col">
<!--window content-->
<border color=".2" width="100%" height="1" grow="1">
<scroll id="content" pad="10" margin="5" color=".3 .3 .3 .4" height="100%" dir="col" wrap="0" shrink="1">
</scroll>
</border>
<!--footer buttons-->
<node id="footer" height="60" dir="row" justify="flex-end">
<!--<button id="btn-ok" text="Ok" width="100" height="30" margin="5 0 0 0"/>-->
</node>
</border>
</border>
</border>
</layout>
</root>

View File

@@ -9,6 +9,7 @@
#include "settings.h"
#include "serializer.h"
#include "font.h"
#include "node_remote_page.h"
void App::title_update()
{
@@ -1333,6 +1334,32 @@ void App::initLayout()
}
}
auto whatsnew = std::make_shared<NodeRemotePage>();
whatsnew->m_manager = &layout;
whatsnew->init();
whatsnew->load_url("http://localhost:8080/app-content/whatsnew.xml", [this, whatsnew] (bool success) {
if (success)
{
int last_id = Settings::value_or<Serializer::Integer>("whatsnew-id", 0);
if (whatsnew->m_page_id <= g_version_build && whatsnew->m_page_id > last_id)
{
whatsnew->set_title(fmt::format("What's new in version {}", g_version_number));
layout[main_id]->add_child(whatsnew);
}
whatsnew->add_button("Reload", 120, [this, whatsnew](Node*) {
whatsnew->reload();
});
whatsnew->add_button("Read Later", 120, [this, whatsnew](Node*) {
whatsnew->destroy();
});
whatsnew->add_button("Close", 100, [this, whatsnew](Node*) {
Settings::set<Serializer::Integer>("whatsnew-id", whatsnew->m_page_id);
Settings::save();
whatsnew->destroy();
});
}
});
brush_update(true, true);
// hacky thing to make the toolbar buttons not steal events when moving cursor fast

View File

@@ -186,6 +186,7 @@ bool Asset::open_url(const std::string& url, std::function<bool(float)> progress
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &tmp_data);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_data_handler_asset);
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
#ifdef __ANDROID__
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
#endif
@@ -221,7 +222,7 @@ std::future<bool>& Asset::open_url_async(const std::string& url,
m_stop_async = false;
remote_future = std::async([this, url, progress, complete] {
bool ok = open_url(url, progress);
if (ok && complete)
if (complete)
complete(ok);
return ok;
});

View File

@@ -39,14 +39,28 @@ bool LayoutManager::load(const char* path)
m_path = path;
auto old = std::move(m_layouts);
Asset file;
if (!(file.open(path) && file.read_all()))
return false;
tinyxml2::XMLDocument xml;
auto ret = xml.Parse((char*)file.m_data, file.m_len);
auto xml_string = std::string((char*)file.m_data, (size_t)file.m_len);
file.close();
if (!parse(xml_string))
return false;
if (on_loaded)
on_loaded(m_loaded);
m_loaded = true;
return true;
}
bool LayoutManager::parse(const std::string& xml_string) noexcept
{
auto old = std::move(m_layouts);
tinyxml2::XMLDocument xml;
auto ret = xml.Parse(xml_string.c_str(), xml_string.size());
if (ret != tinyxml2::XMLError::XML_SUCCESS)
{
return false;
@@ -70,7 +84,7 @@ bool LayoutManager::load(const char* path)
if (std::find(osv.begin(), osv.end(), PP_OS) == osv.end())
{
LOG("Layout %s not for this os(%s), skipping", id_str, PP_OS)
current = current->NextSiblingElement("layout");
current = current->NextSiblingElement("layout");
continue;
}
}
@@ -105,11 +119,6 @@ bool LayoutManager::load(const char* path)
}
current = current->NextSiblingElement("layout");
}
if (on_loaded)
on_loaded(m_loaded);
m_loaded = true;
return true;
}

View File

@@ -1,15 +1,4 @@
#pragma once
#include "shape.h"
#include "util.h"
#include "shader.h"
#include "font.h"
#include "asset.h"
#include "rtt.h"
#include "bezier.h"
#include "canvas.h"
#include "event.h"
#include <tinyxml2.h>
#include <yoga/Yoga.h>
class LayoutManager
{
@@ -23,6 +12,7 @@ public:
void unload();
void create();
bool load(const char* path);
bool parse(const std::string& xml_string) noexcept;
bool reload();
class Node* operator[](uint16_t id)
{

View File

@@ -1177,6 +1177,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
}
case WM_ACTIVATE:
{
win32_show_cursor(true);
App::I->ui_task_async([=] {
int active = GET_WM_ACTIVATE_STATE(wp, lp);
WacomTablet::I.set_focus(active);

View File

@@ -37,6 +37,7 @@
#include "node_panel_quick.h"
#include "node_tool_bucket.h"
#include "node_panel_animation.h"
#include "node_metadata.h"
void Node::app_redraw()
{
@@ -389,6 +390,16 @@ std::shared_ptr<Node> Node::load_template(const std::string& filename, const std
return ret;
}
std::shared_ptr<Node> Node::parse_template(const std::string& xml_string, const std::string& name) const
{
LayoutManager m;
std::shared_ptr<Node> ret;
if (m.parse(xml_string))
ret = m.get_ref(name.c_str())->m_children[0]->clone();
m.unload();
return ret;
}
void Node::add_child(Node* n)
{
App::I->ui_task([&]
@@ -749,6 +760,8 @@ Node::Node(Node&& o)
m_pos_offset_childred = o.m_pos_offset_childred;
m_clip_uncut = o.m_clip_uncut;
auto_width = o.auto_width;
auto_height = o.auto_height;
}
Node::~Node()
@@ -764,12 +777,14 @@ void Node::SetWidth(float value)
{
YGNodeStyleSetWidth(y_node, value);
m_size.x = value;
auto_width = value == YGUndefined;
app_redraw();
}
void Node::SetWidthP(float value)
{
YGNodeStyleSetWidthPercent(y_node, value);
auto_width = value == YGUndefined;
app_redraw();
}
@@ -777,12 +792,14 @@ void Node::SetHeight(float value)
{
YGNodeStyleSetHeight(y_node, value);
m_size.y = value;
auto_height = value == YGUndefined;
app_redraw();
}
void Node::SetHeightP(float value)
{
YGNodeStyleSetHeightPercent(y_node, value);
auto_height = value == YGUndefined;
app_redraw();
}
@@ -1163,6 +1180,7 @@ void Node::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
{
YGNodeStyleSetWidth(y_node, YGUndefined);
YGNodeStyleSetWidthPercent(y_node, YGUndefined);
auto_width = true;
}
else
{
@@ -1170,6 +1188,7 @@ void Node::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
YGNodeStyleSetWidthPercent(y_node, attr->FloatValue());
else
YGNodeStyleSetWidth(y_node, attr->FloatValue());
auto_width = false;
}
break;
case kAttribute::MinWidth:
@@ -1183,6 +1202,7 @@ void Node::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
{
YGNodeStyleSetHeight(y_node, YGUndefined);
YGNodeStyleSetHeightPercent(y_node, YGUndefined);
auto_height = true;
}
else
{
@@ -1190,6 +1210,7 @@ void Node::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
YGNodeStyleSetHeightPercent(y_node, attr->FloatValue());
else
YGNodeStyleSetHeight(y_node, attr->FloatValue());
auto_height = false;
}
break;
case kAttribute::MinHeight:
@@ -1416,6 +1437,7 @@ void Node::load_internal(const tinyxml2::XMLElement* x_node)
CASE(kWidget::UserManual, NodeUserManual);
CASE(kWidget::ToolBucket, NodeToolBucket);
CASE(kWidget::Timeline, NodeAnimationTimeline);
CASE(kWidget::Metadata, NodeMetadata);
#undef CASE
case kWidget::Ref:
{
@@ -1481,6 +1503,9 @@ void Node::clone_copy(Node* dest) const
dest->m_pos_offset = m_pos_offset;
dest->m_pos_offset_childred = m_pos_offset_childred;
dest->m_clip_uncut = m_clip_uncut;
dest->auto_width = auto_width;
dest->auto_height = auto_height;
}
void Node::clone_children(Node* dest) const

View File

@@ -97,6 +97,7 @@ enum class kWidget : uint16_t
UserManual = const_hash("usermanual"),
ToolBucket = const_hash("tool-bucket"),
Timeline = const_hash("timeline"),
Metadata = const_hash("metadata"),
};
class Node : public std::enable_shared_from_this<Node>
@@ -143,6 +144,9 @@ public:
// it's actually rendering
bool m_on_screen = false;
bool auto_width = true;
bool auto_height = true;
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
Node&& operator=(Node&& o);
@@ -279,6 +283,15 @@ public:
}
return nullptr;
}
template<class T = Node> std::shared_ptr<T> add_child_xml(const std::string& xml_string, const std::string& name)
{
if (auto t = parse_template(xml_string, name))
{
add_child(t);
return std::dynamic_pointer_cast<T>(t);
}
return nullptr;
}
virtual void on_tick(float dt) { };
virtual kEventResult on_event(Event* e);
@@ -296,6 +309,7 @@ public:
const Node* init_template(const char* id);
bool init_template_file(const std::string& path, const std::string& id);
std::shared_ptr<Node> load_template(const std::string& filename, const std::string& name) const;
std::shared_ptr<Node> parse_template(const std::string& xml_stirng, const std::string& name) const;
void tick(float dt);
void app_redraw();
void add_child(Node* n);

25
src/node_metadata.cpp Normal file
View File

@@ -0,0 +1,25 @@
#include "pch.h"
#include "log.h"
#include "node_metadata.h"
NodeMetadata::NodeMetadata() noexcept
{
m_nodeID = const_hash("metadata");
m_nodeID_s = "metadata";
}
Node* NodeMetadata::clone_instantiate() const
{
return new NodeMetadata();
}
void NodeMetadata::clone_copy(Node* dest) const
{
Node::clone_copy(dest);
NodeMetadata* n = static_cast<NodeMetadata*>(dest);
n->m_props = m_props;
}
void NodeMetadata::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
{
Node::parse_attributes(ka, attr);
m_props[attr->Name()] = attr->Value();
}

12
src/node_metadata.h Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
#include "node.h"
class NodeMetadata : public Node
{
public:
std::map<std::string, std::string> m_props;
NodeMetadata() noexcept;
virtual Node* clone_instantiate() const override;
virtual void clone_copy(Node* dest) const override;
virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override;
};

98
src/node_remote_page.cpp Normal file
View File

@@ -0,0 +1,98 @@
#include "pch.h"
#include "log.h"
#include "node_remote_page.h"
#include "node_button.h"
#include "node_scroll.h"
#include "asset.h"
#include "node_text.h"
#include "node_metadata.h"
Node* NodeRemotePage::clone_instantiate() const
{
return new NodeRemotePage();
}
void NodeRemotePage::clone_copy(Node* dest) const
{
auto n = static_cast<NodeRemotePage*>(dest);
n->m_page_id = m_page_id;
n->m_loaded = m_loaded;
}
void NodeRemotePage::init()
{
init_template_file("data/dialogs/remote-page.xml", "remote-page");
init_controls();
}
void NodeRemotePage::init_controls()
{
m_content = find<NodeScroll>("content");
m_title = find<NodeText>("title");
}
std::future<bool> NodeRemotePage::load_url(const std::string& url,
std::function<void(bool success)> on_complete /*= nullptr*/) noexcept
{
m_loading = true;
auto align = m_content->add_child_ref<Node>();
align->SetWidthP(100.f);
align->SetHeightP(100.f);
align->SetAlign(YGAlignCenter);
align->SetJustify(YGJustifyCenter);
auto text = align->add_child_ref<NodeText>();
text->set_font(kFont::Arial_30);
text->set_text("Connecting to the server...");
m_url = url;
m_loaded = false;
auto remote = std::make_shared<Asset>();
auto prom = std::make_shared<std::promise<bool>>();
remote->open_url_async(url, nullptr, [this, remote, align, text, prom, on_complete](bool success) {
if (success)
{
m_content->add_child_xml(std::string((char*)remote->m_data, (size_t)remote->m_len), "about");
if (auto meta = m_content->find<NodeMetadata>("metadata"))
m_page_id = std::stol(meta->m_props["page-id"]);
align->destroy();
}
else
{
text->set_text("Error loading the page");
}
m_loaded = success;
m_loading = false;
prom->set_value(success);
if (on_complete)
on_complete(success);
});
return prom->get_future();
}
void NodeRemotePage::reload() noexcept
{
if (m_loading)
return;
m_content->remove_all_children();
load_url(m_url);
}
void NodeRemotePage::set_title(const std::string& title)
{
m_title->set_text(title);
}
void NodeRemotePage::add_button(const std::string& label, int width,
std::function<void(Node* target)> onclic /*= nullptr*/) noexcept
{
if (auto footer = find("footer"))
{
auto b = footer->add_child<NodeButton>();
b->m_text->set_text(label);
b->m_border->SetWidth(width);
b->m_border->SetHeight(30);
b->m_border->SetMargin(5, 0, 0, 0);
b->on_click = onclic;
}
}

23
src/node_remote_page.h Normal file
View File

@@ -0,0 +1,23 @@
#pragma once
#include "node_border.h"
class NodeRemotePage : public NodeBorder
{
class NodeScroll* m_content;
class NodeText* m_title;
bool m_loading;
std::string m_url;
public:
bool m_loaded;
int m_page_id;
virtual Node* clone_instantiate() const override;
virtual void clone_copy(Node* dest) const override;
virtual void init() override;
void init_controls();
std::future<bool> load_url(const std::string& url,
std::function<void(bool success)> on_complete = nullptr) noexcept;
void reload() noexcept;
void set_title(const std::string& title);
void add_button(const std::string& label, int width,
std::function<void(Node* target)> onclic = nullptr) noexcept;
};

View File

@@ -20,6 +20,10 @@ void NodeText::clone_copy(Node* dest) const
n->m_color = m_color;
n->m_font_size = m_font_size;
n->font_id = font_id;
n->m_multiline = m_multiline;
n->m_off = m_off;
n->m_text_align_v = m_text_align_v;
n->m_text_align_h = m_text_align_h;
}
void NodeText::create()
@@ -32,7 +36,7 @@ void NodeText::create()
font_id = (kFont)const_hash(font);
m_text_mesh.create();
m_text_mesh.update(font_id, m_text);
SetSize(m_text_mesh.bb);
update_layout();
}
}
@@ -41,13 +45,7 @@ void NodeText::set_font(kFont fontID)
font_id = fontID;
m_text_mesh.create();
m_text_mesh.update(font_id, m_text);
SetSize(m_text_mesh.bb);
}
void NodeText::restore_context()
{
Node::restore_context();
create();
update_layout();
}
void NodeText::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
@@ -55,6 +53,25 @@ void NodeText::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* att
Node::parse_attributes(ka, attr);
switch (ka)
{
case kAttribute::Multiline:
m_multiline = attr->BoolValue();
break;
case kAttribute::TextAlign:
if (strcmp(attr->Value(), "left") == 0)
m_text_align_h = TextAlign::Begin;
else if (strcmp(attr->Value(), "center") == 0)
m_text_align_h = TextAlign::Center;
else if (strcmp(attr->Value(), "right") == 0)
m_text_align_h = TextAlign::End;
break;
case kAttribute::TextVerticalAlign:
if (strcmp(attr->Value(), "top") == 0)
m_text_align_v = TextAlign::Begin;
else if (strcmp(attr->Value(), "center") == 0)
m_text_align_v = TextAlign::Center;
else if (strcmp(attr->Value(), "bottom") == 0)
m_text_align_v = TextAlign::End;
break;
case kAttribute::TextWrapWidth:
m_text_mesh.max_width = attr->IntValue();
break;
@@ -82,11 +99,11 @@ void NodeText::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* att
}
}
void NodeText::set_text(const char* s)
void NodeText::set_text(const std::string& s)
{
m_text = s;
m_text_mesh.update(font_id, s);
SetSize(m_text_mesh.bb);
update_layout();
}
void NodeText::set_text_format(const char* fmt, ...)
@@ -98,7 +115,39 @@ void NodeText::set_text_format(const char* fmt, ...)
va_end(args);
m_text = buffer;
m_text_mesh.update(font_id, buffer);
SetSize(m_text_mesh.bb);
update_layout();
}
void NodeText::update_layout()
{
if (auto_width)
{
YGNodeStyleSetWidth(y_node, m_text_mesh.bb.x);
m_size.x = m_text_mesh.bb.x;
}
if (auto_height)
{
YGNodeStyleSetHeight(y_node, m_text_mesh.bb.y);
m_size.y = m_text_mesh.bb.y;
}
auto pad = GetPadding();
float h = GetHeight() - (pad[1] + pad[3]);
float w = GetWidth() - (pad[0] + pad[2]);
if (m_text_align_v == TextAlign::Begin)
m_off.y = pad[0];
else if (m_text_align_v == TextAlign::Center)
m_off.y = (h - m_text_mesh.bb.y) * 0.5f + pad[0];
else
m_off.y = (h - m_text_mesh.bb.y) + pad[0];
if (m_text_align_h == TextAlign::Begin)
m_off.x = pad[3];
else if (m_text_align_v == TextAlign::Center)
m_off.x = (w - m_text_mesh.bb.x) * 0.5f + pad[3];
else
m_off.x = (w - m_text_mesh.bb.x) + pad[3];
}
void NodeText::draw()
@@ -116,6 +165,8 @@ void NodeText::draw()
void NodeText::handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom)
{
auto pad = GetPadding();
m_text_mesh.max_width = m_multiline ? new_size.x - (pad[1] + pad[3]) : 0;
m_text_mesh.update(font_id, m_text);
SetSize(m_text_mesh.bb);
update_layout();
}

View File

@@ -5,20 +5,33 @@
class NodeText : public Node
{
public:
enum class TextAlign
{
Begin,
Center,
End,
};
TextMesh m_text_mesh;
std::string m_text;
std::string m_font = "arial";
glm::vec4 m_color{ 1, 1, 1, 1 };
int m_font_size = 11;
kFont font_id;
bool m_multiline = false;
glm::vec2 m_off;
TextAlign m_text_align_v = TextAlign::Center;
TextAlign m_text_align_h = TextAlign::Begin;
virtual Node* clone_instantiate() const override;
virtual void clone_copy(Node* dest) const override;
virtual void create() override;
virtual void restore_context() override;
virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override;
void set_text(const char* s);
void set_text_format(const char* fmt, ...);
void set_font(kFont fontID);
virtual void draw() override;
virtual void handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom) override;
void set_text(const std::string& s);
void set_text_format(const char* fmt, ...);
void set_font(kFont fontID);
void update_layout();
};

View File

@@ -33,9 +33,6 @@ void NodeTextInput::clone_copy(Node* dest) const
{
NodeBorder::clone_copy(dest);
NodeTextInput* n = static_cast<NodeTextInput*>(dest);
n->m_multiline = m_multiline;
n->m_text_mesh.max_width = m_text_mesh.max_width;
n->m_text_mesh.create();
n->m_text_mesh.update(font_id, m_text);
@@ -44,6 +41,7 @@ void NodeTextInput::clone_copy(Node* dest) const
n->m_color = m_color;
n->m_font_size = m_font_size;
n->font_id = font_id;
n->m_multiline = m_multiline;
n->m_off = m_off;
n->m_text_align_v = m_text_align_v;
n->m_text_align_h = m_text_align_h;

View File

@@ -20,6 +20,7 @@ public:
glm::vec4 m_color{ 1, 1, 1, 1 };
int m_font_size = 11;
kFont font_id;
bool m_multiline = false;
bool m_cursor_visible = false;
glm::vec2 m_off;