1 //------------------------------------------------------------------------------ 2 // droptest.d 3 // Test drag'n'drop file loading. 4 //------------------------------------------------------------------------------ 5 6 module examples.droptest; 7 8 private: 9 10 import sg = sokol.gfx; 11 import sgapp = sokol.glue; 12 import sapp = sokol.app; 13 import sfetch = sokol.fetch; 14 import simgui = sokol.imgui; 15 import log = sokol.log; 16 import imgui; 17 18 enum MAX_FILE_SIZE = 1024 * 1024; 19 20 enum LoadState 21 { 22 Unknown = 0, 23 Success, 24 Failed, 25 FileTooBig, 26 } 27 28 struct State 29 { 30 LoadState load_state = LoadState.Unknown; 31 uint size = 0; 32 ubyte[MAX_FILE_SIZE] buffer = 0; 33 } 34 35 static State state; 36 37 extern (C) void init() @safe @nogc nothrow 38 { 39 sg.Desc gfx = { 40 environment: sgapp.environment, 41 logger: {func: &log.slog_func} 42 }; 43 sg.setup(gfx); 44 simgui.Desc imgui_desc = {0}; 45 simgui.setup(imgui_desc); 46 47 // ifndef emscripten 48 // dfmt off 49 static if((){version(Emscripten) return false; else return true;}()) 50 { 51 sfetch.Desc fetch_desc = { 52 num_channels: 1, 53 num_lanes: 1, 54 logger: {func: &log.slog_func}, 55 }; 56 sfetch.setup(fetch_desc); 57 } 58 // dfmt on 59 } 60 61 extern (C) void frame() @trusted 62 { 63 // ifndef emscripten 64 // dfmt off 65 static if((){version(Emscripten) return false;else return true;}()) 66 { 67 sfetch.dowork; 68 } 69 simgui.FrameDesc imgui_desc = { 70 width: sapp.width(), 71 height: sapp.height(), 72 delta_time: sapp.frameDuration(), 73 dpi_scale: sapp.dpiScale(), 74 }; 75 // dfmt on 76 simgui.newFrame(imgui_desc); 77 78 // /*=== UI CODE STARTS HERE ===*/ 79 const ImVec2 window_pos = {10, 10}; 80 const ImVec2 window_size = {600, 500}; 81 SetNextWindowPos(window_pos, ImGuiCond_.ImGuiCond_Once); 82 SetNextWindowSize(window_size, ImGuiCond_.ImGuiCond_Once); 83 Begin("Drop a file!".ptr, null, ImGuiWindowFlags_.ImGuiWindowFlags_None); 84 if (state.load_state != LoadState.Unknown) 85 { 86 Text("%s:", sapp.getDroppedFilePath(0)); 87 } 88 switch (state.load_state) 89 { 90 case LoadState.Failed: 91 Text("LOAD FAILED!"); 92 break; 93 case LoadState.FileTooBig: 94 Text("FILE TOO BIG!"); 95 break; 96 case LoadState.Success: 97 Separator; 98 renderFileContent; 99 break; 100 default: 101 break; 102 } 103 End(); 104 /*=== UI CODE ENDS HERE ===*/ 105 106 sg.Pass pass = {swapchain: sgapp.swapchain}; 107 sg.beginPass(pass); 108 simgui.render; 109 sg.endPass; 110 sg.commit; 111 } 112 113 extern (C) void event(const(sapp.Event)* ev) @trusted @nogc nothrow 114 { 115 simgui.simgui_handle_event(ev); 116 if (ev.type == sapp.EventType.Files_dropped) 117 { 118 version (Emscripten) 119 { 120 // on emscripten need to use the sokol-app helper function to load the file data 121 sapp.Html5FetchRequest req = { 122 dropped_file_index: 0, 123 callback: &emsc_load_callback, 124 buffer: {ptr: &state.buffer[0], size: state.buffer.sizeof}}; 125 sapp.html5FetchDroppedFile(req); 126 } 127 else 128 { 129 // native platform: use sokol-fetch to load file content 130 sfetch.Request req = { 131 path: sapp.getDroppedFilePath(0), 132 callback: &native_load_callback, 133 buffer: {ptr: &state.buffer[0], size: state.buffer.sizeof} 134 }; 135 sfetch.send(req); 136 } 137 } 138 } 139 140 extern (C) void cleanup() @safe @nogc nothrow 141 { 142 // ifndef emscripten 143 // dfmt off 144 static if((){version(Emscripten) return false;else return true;}()) 145 { 146 sfetch.shutdown; 147 } 148 // dfmt on 149 simgui.shutdown; 150 sg.shutdown; 151 } 152 153 extern (C) void main() @safe @nogc nothrow 154 { 155 // dfmt off 156 sapp.Desc runner = { 157 window_title: "droptest.d", 158 init_cb: &init, 159 frame_cb: &frame, 160 cleanup_cb: &cleanup, 161 event_cb: &event, 162 width: 800, 163 height: 600, 164 enable_dragndrop: true, 165 max_dropped_files: 1, 166 icon: {sokol_default: true}, 167 logger: {func: &log.func} 168 }; 169 // dfmt on 170 sapp.run(runner); 171 } 172 173 void renderFileContent() 174 { 175 immutable int bytes_per_line = 16; // keep this 2^N 176 immutable int num_lines = (state.size + (bytes_per_line - 1)) / bytes_per_line; 177 ImVec2 sz = {0, 0}; 178 BeginChild("##scrolling", sz, false, ImGuiWindowFlags_ 179 .ImGuiWindowFlags_NoMove | ImGuiWindowFlags_ 180 .ImGuiWindowFlags_NoNav); 181 ImGuiListClipper* clipper = null; 182 ImGuiListClipper_Begin(clipper, num_lines, GetTextLineHeight()); 183 ImGuiListClipper_Step(clipper); 184 for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) 185 { 186 int start_offset = line_i * bytes_per_line; 187 int end_offset = start_offset + bytes_per_line; 188 if (end_offset >= state.size) 189 { 190 end_offset = state.size; 191 } 192 Text("%04X: ", start_offset); 193 for (int i = start_offset; i < end_offset; i++) 194 { 195 SameLineEx(0.0f, 0.0f); 196 Text("%02X ", state.buffer[i]); 197 } 198 SameLineEx((6 * 7.0f) + (bytes_per_line * 3 * 7.0f) + (2 * 7.0f), 0.0f); 199 for (int i = start_offset; i < end_offset; i++) 200 { 201 if (i != start_offset) 202 { 203 SameLineEx(0.0f, 0.0f); 204 } 205 ubyte c = state.buffer[i]; 206 if ((c < 32) || (c > 127)) 207 { 208 c = '.'; 209 } 210 Text("%c", c); 211 } 212 } 213 Text("EOF\n"); 214 ImGuiListClipper_End(clipper); 215 EndChild(); 216 } 217 218 version (Emscripten) 219 { 220 // the async-loading callback for sapp_html5_fetch_dropped_file 221 extern (C) void emsc_load_callback(const(sapp.Html5FetchResponse*) response) @nogc nothrow 222 { 223 if (response.succeeded) 224 { 225 state.load_state = LoadState.Success; 226 state.size = cast(uint) response.data.size; 227 } 228 else if (response.error_code == sapp.Html5FetchError.Fetch_error_buffer_too_small) 229 { 230 state.load_state = LoadState.FileTooBig; 231 } 232 else 233 { 234 state.load_state = LoadState.Failed; 235 } 236 } 237 } 238 else 239 { 240 // the async-loading callback for native platforms 241 extern (C) void native_load_callback(const(sfetch.Response*) response) @nogc nothrow 242 { 243 if (response.fetched) 244 { 245 state.load_state = LoadState.Success; 246 state.size = cast(uint) response.data.size; 247 } 248 else if (response.error_code == sfetch.Error.Buffer_too_small) 249 { 250 state.load_state = LoadState.FileTooBig; 251 } 252 else 253 { 254 state.load_state = LoadState.Failed; 255 } 256 } 257 }