Skip to content

Commit 853c1cc

Browse files
committed
(Failed) attempt to add copy and pasting
1 parent f3271a2 commit 853c1cc

6 files changed

Lines changed: 177 additions & 61 deletions

File tree

dependencies/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ add_library(learnopengl
4343
"learnopengl/src/shader.cpp"
4444
"learnopengl/include/shader/shader.h"
4545
"learnopengl/include/stb_image/stb_image.h"
46-
"learnopengl/src/stb_image.cpp" "learnopengl/include/camera/camera.h" "learnopengl/src/camera.cpp" "learnopengl/include/VAO/VAO.h" "learnopengl/include/VBO/VBO.h" "learnopengl/src/VAO.cpp" "learnopengl/src/VBO.cpp" "learnopengl/include/texture/texture.h" "learnopengl/src/texture.cpp" "learnopengl/include/stb_image/stb_image_write.h" "learnopengl/src/stb_image_write.cpp")
46+
"learnopengl/src/stb_image.cpp" "learnopengl/include/camera/camera.h" "learnopengl/src/camera.cpp" "learnopengl/include/VAO/VAO.h" "learnopengl/include/VBO/VBO.h" "learnopengl/src/VAO.cpp" "learnopengl/src/VBO.cpp" "learnopengl/include/texture/texture.h" "learnopengl/src/texture.cpp" "learnopengl/include/stb_image/stb_image_write.h" "learnopengl/src/stb_image_write.cpp" "learnopengl/include/emscripten_browser_clipboard.h")
4747

4848
target_include_directories(learnopengl PUBLIC learnopengl/include)
4949
if(EMSCRIPTEN)
@@ -64,13 +64,15 @@ add_library(imgui STATIC
6464
${imgui_SOURCE_DIR}/imgui_draw.cpp
6565
${imgui_SOURCE_DIR}/imgui_tables.cpp
6666
${imgui_SOURCE_DIR}/imgui_widgets.cpp
67+
${imgui_SOURCE_DIR}/misc/cpp/imgui_stdlib.cpp
6768
${imgui_SOURCE_DIR}/backends/imgui_impl_glfw.cpp
6869
${imgui_SOURCE_DIR}/backends/imgui_impl_opengl3.cpp
6970
)
7071

7172
target_include_directories(imgui PUBLIC
7273
${imgui_SOURCE_DIR}
7374
${imgui_SOURCE_DIR}/backends
75+
${imgui_SOURCE_DIR}/misc/cpp
7476
)
7577

7678
if(EMSCRIPTEN)
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#include <iostream>
2+
#ifdef __EMSCRIPTEN__
3+
4+
#ifndef EMSCRIPTEN_BROWSER_CLIPBOARD_H_INCLUDED
5+
#define EMSCRIPTEN_BROWSER_CLIPBOARD_H_INCLUDED
6+
7+
#include <string>
8+
#include <emscripten.h>
9+
10+
#define _EM_JS_INLINE(ret, c_name, js_name, params, code) \
11+
extern "C" { \
12+
ret c_name params EM_IMPORT(js_name); \
13+
EMSCRIPTEN_KEEPALIVE \
14+
__attribute__((section("em_js"), aligned(1))) inline char __em_js__##js_name[] = \
15+
#params "<::>" code; \
16+
}
17+
18+
#define EM_JS_INLINE(ret, name, params, ...) _EM_JS_INLINE(ret, name, name, params, #__VA_ARGS__)
19+
20+
namespace emscripten_browser_clipboard {
21+
22+
/////////////////////////////////// Interface //////////////////////////////////
23+
24+
using paste_handler = void(*)(std::string const&, void*);
25+
using copy_handler = char const* (*)(void*);
26+
27+
inline void paste(paste_handler callback, void* callback_data = nullptr);
28+
inline void copy(copy_handler callback, void* callback_data = nullptr);
29+
inline void copy(std::string const& content);
30+
31+
///////////////////////////////// Implementation ///////////////////////////////
32+
33+
namespace {
34+
35+
EM_JS_INLINE(void, paste_js, (paste_handler callback, void* callback_data), {
36+
/// Register the given callback to handle paste events. Callback data pointer is passed through to the callback.
37+
/// Paste handler callback signature is:
38+
/// void my_handler(std::string const &paste_data, void *callback_data = nullptr);
39+
document.addEventListener('paste', function (event) {
40+
Module["ccall"]('paste_return', 'number',['string', 'number', 'number'],[event.clipboardData.getData('text/plain'), callback, callback_data]);
41+
});
42+
});
43+
44+
EM_JS_INLINE(void, copy_js, (copy_handler callback, void* callback_data), {
45+
/// Register the given callback to handle copy events. Callback data pointer is passed through to the callback.
46+
/// Copy handler callback signature is:
47+
/// char const *my_handler(void *callback_data = nullptr);
48+
document.addEventListener('copy', function (event){
49+
const content_ptr = Module["ccall"]('copy_return', 'number',['number', 'number'],[callback, callback_data]);
50+
event.clipboardData.setData('text/plain', UTF8ToString(content_ptr));
51+
event.preventDefault();
52+
});
53+
});
54+
55+
EM_JS_INLINE(void, copy_async_js, (char const* content_ptr), {
56+
/// Attempt to copy the provided text asynchronously
57+
navigator.clipboard.writeText(UTF8ToString(content_ptr));
58+
});
59+
60+
}
61+
62+
inline void paste(paste_handler callback, void* callback_data) {
63+
/// C++ wrapper for javascript paste call
64+
paste_js(callback, callback_data);
65+
}
66+
67+
inline void copy(copy_handler callback, void* callback_data) {
68+
/// C++ wrapper for javascript copy call
69+
copy_js(callback, callback_data);
70+
}
71+
72+
inline void copy(std::string const& content) {
73+
/// C++ wrapper for javascript copy call
74+
copy_async_js(content.c_str());
75+
}
76+
77+
namespace {
78+
79+
extern "C" {
80+
81+
EMSCRIPTEN_KEEPALIVE inline int paste_return(char const* paste_data, paste_handler callback, void* callback_data);
82+
83+
EMSCRIPTEN_KEEPALIVE inline int paste_return(char const* paste_data, paste_handler callback, void* callback_data) {
84+
/// Call paste callback - this function is called from javascript when the paste event occurs
85+
callback(paste_data, callback_data);
86+
return 1;
87+
}
88+
89+
EMSCRIPTEN_KEEPALIVE inline char const* copy_return(copy_handler callback, void* callback_data);
90+
91+
EMSCRIPTEN_KEEPALIVE inline char const* copy_return(copy_handler callback, void* callback_data) {
92+
/// Call paste callback - this function is called from javascript when the paste event occurs
93+
return callback(callback_data);
94+
}
95+
96+
}
97+
98+
}
99+
100+
}
101+
102+
#endif // EMSCRIPTEN_BROWSER_CLIPBOARD_H_INCLUDED
103+
104+
#endif

src/CMakeLists.txt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,17 @@ target_include_directories(complex-plotter PRIVATE
7878
)
7979

8080
if(EMSCRIPTEN)
81-
set(EMSCRIPTEN_LINK_FLAGS "-s USE_GLFW=3 -s USE_WEBGL2=1 -s MIN_WEBGL_VERSION=2 -s MAX_WEBGL_VERSION=2 -s ALLOW_MEMORY_GROWTH=1 -fexceptions -s ASSERTIONS=1")
82-
set_target_properties(complex-plotter PROPERTIES LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS}")
81+
target_link_options(complex-plotter PRIVATE
82+
--bind
83+
-s USE_GLFW=3
84+
-sUSE_WEBGL2=1
85+
-sMIN_WEBGL_VERSION=2
86+
-sMAX_WEBGL_VERSION=2
87+
-sALLOW_MEMORY_GROWTH=1
88+
-fexceptions
89+
-sASSERTIONS=1
90+
-sASYNCIFY=1
91+
-sEXPORTED_RUNTIME_METHODS=[ccall])
8392
target_compile_options(complex-plotter PRIVATE "-s" "USE_GLFW=3" "-fexceptions")
8493
set_target_properties(complex-plotter PROPERTIES SUFFIX ".js")
8594
add_custom_command(TARGET complex-plotter POST_BUILD

src/graphics/ui.cpp

Lines changed: 45 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
#include <string>
77
#include <iostream>
88

9+
#include <imgui.h>
10+
#include <imgui_stdlib.h>
11+
912
#include <interactions/export.h>
1013

1114

@@ -17,15 +20,15 @@ const float DEBOUNCE_DELAY = 0.05f;
1720
namespace UI {
1821
bool Button(const char* label, const ImVec2& size = ImVec2(0, 0)) {
1922
bool clicked = ImGui::Button(label, size);
20-
23+
2124
if (ImGui::IsItemHovered()) {
2225
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
2326
}
2427
return clicked;
2528
}
2629

2730
bool RadioButton(const char* label, const bool cond) {
28-
bool clicked = ImGui::RadioButton(label,cond);
31+
bool clicked = ImGui::RadioButton(label, cond);
2932
if (ImGui::IsItemHovered()) {
3033
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
3134
}
@@ -48,56 +51,44 @@ namespace UI {
4851
}
4952

5053

51-
int input_text_callback(ImGuiInputTextCallbackData* data) {
52-
if (data->EventFlag != ImGuiInputTextFlags_CallbackResize) {
53-
return 0;
54-
}
55-
string* s = static_cast<string*>(data->UserData);
56-
s->resize(data->BufTextLen);
57-
data->Buf = static_cast<char*>(s->data());
58-
return 0;
59-
}
60-
61-
62-
6354
void render(FunctionState& state, unsigned int& op_tex, unsigned int& const_tex, Shader& interpreter_shader) {
64-
try {
65-
vector<TokenOperator> stack = parser::parse(state.expression);
66-
vector<unsigned char> operator_stack;
67-
vector<vec2> constant_stack;
68-
get_stacks(stack, operator_stack, constant_stack);
69-
unsigned int op_tbo_buf, const_tbo_buf;
70-
populate_texture(op_tbo_buf, op_tex, operator_stack);
71-
populate_texture(const_tbo_buf, const_tex, constant_stack);
72-
state.error_message = "";
73-
state.needs_reparse = false;
74-
}
75-
catch (const std::runtime_error& e) {
76-
state.error_message = e.what();
77-
state.needs_reparse = false;
78-
}
79-
state.current_shader = &interpreter_shader;
80-
state.is_interpreted = true;
55+
try {
56+
vector<TokenOperator> stack = parser::parse(state.expression);
57+
vector<unsigned char> operator_stack;
58+
vector<vec2> constant_stack;
59+
get_stacks(stack, operator_stack, constant_stack);
60+
unsigned int op_tbo_buf, const_tbo_buf;
61+
populate_texture(op_tbo_buf, op_tex, operator_stack);
62+
populate_texture(const_tbo_buf, const_tex, constant_stack);
63+
state.error_message = "";
64+
state.needs_reparse = false;
65+
}
66+
catch (const std::runtime_error& e) {
67+
state.error_message = e.what();
68+
state.needs_reparse = false;
69+
}
70+
state.current_shader = &interpreter_shader;
71+
state.is_interpreted = true;
8172
}
8273

8374
void compile(FunctionState& state, CompilerShader& compiler_shader, unsigned int& op_tex, unsigned int& const_tex) {
84-
try {
85-
vector<TokenOperator> stack = parser::parse(state.expression);
75+
try {
76+
vector<TokenOperator> stack = parser::parse(state.expression);
8677
vector<unsigned char> operator_stack;
8778
vector<vec2> constant_stack;
8879
get_stacks(stack, operator_stack, constant_stack);
8980
unsigned int op_tbo_buf, const_tbo_buf;
9081
populate_texture(op_tbo_buf, op_tex, operator_stack);
9182
populate_texture(const_tbo_buf, const_tex, constant_stack);
92-
const string& expression = stack_to_glsl_string(stack);
93-
compiler_shader.compile(expression,state.is_3d);
94-
}
95-
catch (const std::runtime_error& e) {
96-
state.error_message = e.what();
97-
state.needs_reparse = false;
98-
}
99-
state.current_shader = &compiler_shader.shader;
100-
state.is_interpreted = false;
83+
const string& expression = stack_to_glsl_string(stack);
84+
compiler_shader.compile(expression, state.is_3d);
85+
}
86+
catch (const std::runtime_error& e) {
87+
state.error_message = e.what();
88+
state.needs_reparse = false;
89+
}
90+
state.current_shader = &compiler_shader.shader;
91+
state.is_interpreted = false;
10192
}
10293

10394

@@ -116,7 +107,7 @@ void render_inspector_overlay(const PickerResult& hover, ViewState& view_state)
116107
ImVec2 window_pos, window_pos_pivot;
117108
window_pos.x = work_pos.x + work_size.x - padding;
118109
window_pos.y = work_pos.y + work_size.y - padding;
119-
window_pos_pivot = ImVec2(1.0f, 1.0f);
110+
window_pos_pivot = ImVec2(1.0f, 1.0f);
120111
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
121112
}
122113
ImGui::SetNextWindowBgAlpha(0.65f);
@@ -168,11 +159,8 @@ void render_and_update(FunctionState& state, ViewState& view_state, unsigned int
168159
static bool auto_reparse = true;
169160

170161
bool pressed_enter = ImGui::InputText("##source",
171-
static_cast<char*>(state.expression.data()),
172-
state.expression.capacity() + 1,
173-
ImGuiInputTextFlags_CallbackResize | ImGuiInputTextFlags_EnterReturnsTrue,
174-
input_text_callback,
175-
&state.expression);
162+
&state.expression,
163+
ImGuiInputTextFlags_EnterReturnsTrue);
176164

177165
bool typed = ImGui::IsItemEdited();
178166

@@ -183,7 +171,7 @@ void render_and_update(FunctionState& state, ViewState& view_state, unsigned int
183171

184172
ImGui::SameLine();
185173
if (UI::Button("Compile")) {
186-
pressed_enter = true;
174+
pressed_enter = true;
187175
}
188176
if (ImGui::IsItemHovered()) {
189177
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
@@ -277,9 +265,9 @@ void render_and_update(FunctionState& state, ViewState& view_state, unsigned int
277265
"sin(z)*cos(10/z)"
278266
};
279267
static int current_preset = -1;
280-
if (ImGui::Combo("Choose", & current_preset, presets, IM_ARRAYSIZE(presets))) {
268+
if (ImGui::Combo("Choose", &current_preset, presets, IM_ARRAYSIZE(presets))) {
281269
state.expression = string(presets[current_preset]);
282-
state.needs_reparse = true;
270+
state.needs_reparse = true;
283271
}
284272
}
285273
if (UI::CollapsingHeader("Export")) {
@@ -310,7 +298,7 @@ void render_and_update(FunctionState& state, ViewState& view_state, unsigned int
310298
}
311299
if (view_state.show_export_success) {
312300
ImGui::OpenPopup("Export Successful!");
313-
view_state.show_export_success = false;
301+
view_state.show_export_success = false;
314302
}
315303

316304
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
@@ -321,19 +309,19 @@ void render_and_update(FunctionState& state, ViewState& view_state, unsigned int
321309
ImGui::Spacing();
322310
ImGui::Separator();
323311
ImGui::Spacing();
324-
#ifndef __EMSCRIPTEN__
312+
#ifndef __EMSCRIPTEN__
325313
ImGui::TextDisabled("Check the folder where your executable is located.");
326-
#else
314+
#else
327315
ImGui::TextDisabled("Check your browser's downloads folder.");
328-
#endif
316+
#endif
329317
ImGui::Spacing();
330-
318+
331319
ImGui::SetCursorPosX((ImGui::GetWindowSize().x - 120.0f) * 0.5f);
332320
if (UI::Button("Awesome", ImVec2(120, 0))) {
333321
ImGui::CloseCurrentPopup();
334322
}
335323
ImGui::SetItemDefaultFocus();
336-
324+
337325
ImGui::EndPopup();
338326
}
339327

src/index.html

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,9 @@
5555

5656
<div id="loading-overlay">
5757
<div class="spinner"></div>
58-
<h2>Loading...</h2>
58+
<h2>Loading!...</h2>
5959
</div>
60+
6061
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
6162
<script type="text/javascript">
6263
const canvas = document.getElementById('canvas');
@@ -67,16 +68,26 @@ <h2>Loading...</h2>
6768

6869
var Module = {
6970
canvas: canvas,
71+
print: function(text) {
72+
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
73+
console.log(text);
74+
},
75+
printErr: function(text) {
76+
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
77+
console.error(text);
78+
},
7079
setCanvasSize: function(width, height, noRepaint) {
7180
const dpr = window.devicePixelRatio || 1;
7281
this.canvas.width = width * dpr;
7382
this.canvas.height = height * dpr;
7483
},
7584
onRuntimeInitialized: function() {
7685
document.getElementById('loading-overlay').style.display = 'none';
86+
canvas.focus();
7787
}
7888
};
7989
</script>
90+
<textarea id="clipping" style="position:absolute; width:0; height:0; border:0; opacity:0; pointer-events:none;" aria-hidden="true"></textarea>
8091

8192
<script async type="text/javascript" src="complex-plotter.js"></script>
8293
</body>

src/main.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ void export_to_png(AppContext* ctx, int target_width, int target_height, const c
142142
stbi_write_png(filename, target_width, target_height, 4, pixels, target_width * 4);
143143
delete[] pixels;
144144

145+
const int hours_wasted = 4;
146+
145147
#ifdef __EMSCRIPTEN__
146148
EM_ASM_({
147149
const js_filename = UTF8ToString($0);

0 commit comments

Comments
 (0)