1 //------------------------------------------------------------------------------ 2 // droptest.d 3 // Test drag'n'drop file loading. 4 //------------------------------------------------------------------------------ 5 6 module examples.droptest; 7 8 import sg = sokol.gfx; 9 import sgapp = sokol.glue; 10 import sapp = sokol.app; 11 import sfetch = sokol.fetch; 12 import imgui = sokol.imgui; 13 import log = sokol.log; 14 15 enum MAX_FILE_SIZE = 1024 * 1024; 16 17 enum LoadState 18 { 19 Unknown = 0, 20 Success, 21 Failed, 22 FileTooBig, 23 } 24 25 struct State 26 { 27 LoadState load_state = LoadState.Unknown; 28 uint size = 0; 29 ubyte[MAX_FILE_SIZE] buffer = 0; 30 } 31 32 static State state; 33 34 extern (C) void init() @safe @nogc nothrow 35 { 36 sg.Desc gfx = { 37 environment: sgapp.environment, 38 logger: {func: &log.slog_func} 39 }; 40 sg.setup(gfx); 41 imgui.Desc imgui_desc = {0}; 42 imgui.setup(imgui_desc); 43 44 // ifndef emscripten 45 static if((){version(Emscripten) return false; else return true;}()) 46 { 47 sfetch.Desc fetch_desc = { 48 num_channels: 1, 49 num_lanes: 1, 50 logger: {func: &log.slog_func}, 51 }; 52 sfetch.setup(fetch_desc); 53 } 54 } 55 56 extern (C) void frame() @trusted @nogc nothrow 57 { 58 // ifndef emscripten 59 static if((){version(Emscripten) return false;else return true;}()) 60 { 61 sfetch.dowork; 62 } 63 imgui.FrameDesc imgui_desc = { 64 width: sapp.width(), 65 height: sapp.height(), 66 delta_time: sapp.frameDuration(), 67 dpi_scale: sapp.dpiScale(), 68 }; 69 imgui.newFrame(imgui_desc); 70 71 // /*=== UI CODE STARTS HERE ===*/ 72 const ImVec2 window_pos = {10, 10}; 73 const ImVec2 window_pos_pivot = {0, 0}; 74 const ImVec2 window_size = {600, 500}; 75 igSetNextWindowPos(window_pos, ImGuiCond_.ImGuiCond_Once, window_pos_pivot); 76 igSetNextWindowSize(window_size, ImGuiCond_.ImGuiCond_Once); 77 igBegin("Drop a file!".ptr, null, ImGuiWindowFlags_.ImGuiWindowFlags_None); 78 if(state.load_state != LoadState.Unknown) 79 { 80 igText("%s:", sapp.getDroppedFilePath(0)); 81 } 82 switch (state.load_state) { 83 case LoadState.Failed: 84 igText("LOAD FAILED!"); 85 break; 86 case LoadState.FileTooBig: 87 igText("FILE TOO BIG!"); 88 break; 89 case LoadState.Success: 90 igSeparator; 91 renderFileContent; 92 break; 93 default: 94 break; 95 } 96 igEnd(); 97 /*=== UI CODE ENDS HERE ===*/ 98 99 sg.Pass pass = {swapchain: sgapp.swapchain}; 100 sg.beginPass(pass); 101 imgui.render; 102 sg.endPass; 103 sg.commit; 104 } 105 106 extern (C) void event(const(sapp.Event)* ev) @trusted @nogc nothrow 107 { 108 imgui.simgui_handle_event(ev); 109 if (ev.type == sapp.EventType.Files_dropped) 110 { 111 version (Emscripten) 112 { 113 // on emscripten need to use the sokol-app helper function to load the file data 114 sapp.Html5FetchRequest req = { 115 dropped_file_index: 0, 116 callback: &emsc_load_callback, 117 buffer: {ptr: &state.buffer[0], size: state.buffer.sizeof} 118 }; 119 sapp.html5FetchDroppedFile(req); 120 } 121 else 122 { 123 // native platform: use sokol-fetch to load file content 124 sfetch.Request req = { 125 path: sapp.getDroppedFilePath(0), 126 callback: &native_load_callback, 127 buffer: {ptr: &state.buffer[0], size: state.buffer.sizeof} 128 }; 129 sfetch.send(req); 130 } 131 } 132 } 133 134 extern (C) void cleanup() @safe @nogc nothrow 135 { 136 // ifndef emscripten 137 static if((){version(Emscripten) return false;else return true;}()) 138 { 139 sfetch.shutdown; 140 } 141 imgui.shutdown; 142 sg.shutdown; 143 } 144 145 extern (C) void main() @safe @nogc nothrow 146 { 147 sapp.Desc runner = { 148 window_title: "droptest.d", 149 init_cb: &init, 150 frame_cb: &frame, 151 cleanup_cb: &cleanup, 152 event_cb: &event, 153 width: 800, 154 height: 600, 155 enable_dragndrop: true, 156 max_dropped_files: 1, 157 icon: {sokol_default: true}, 158 logger: {func: &log.func} 159 }; 160 sapp.run(runner); 161 } 162 163 void renderFileContent() @nogc nothrow 164 { 165 immutable int bytes_per_line = 16; // keep this 2^N 166 immutable int num_lines = (state.size + (bytes_per_line - 1)) / bytes_per_line; 167 ImVec2 sz = {0, 0}; 168 igBeginChild_Str("##scrolling", sz, false, ImGuiWindowFlags_.ImGuiWindowFlags_NoMove | ImGuiWindowFlags_ 169 .ImGuiWindowFlags_NoNav); 170 ImGuiListClipper* clipper = ImGuiListClipper_ImGuiListClipper(); 171 ImGuiListClipper_Begin(clipper, num_lines, igGetTextLineHeight()); 172 ImGuiListClipper_Step(clipper); 173 for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) 174 { 175 int start_offset = line_i * bytes_per_line; 176 int end_offset = start_offset + bytes_per_line; 177 if (end_offset >= state.size) 178 { 179 end_offset = state.size; 180 } 181 igText("%04X: ", start_offset); 182 for (int i = start_offset; i < end_offset; i++) 183 { 184 igSameLine(0.0f, 0.0f); 185 igText("%02X ", state.buffer[i]); 186 } 187 igSameLine((6 * 7.0f) + (bytes_per_line * 3 * 7.0f) + (2 * 7.0f), 0.0f); 188 for (int i = start_offset; i < end_offset; i++) 189 { 190 if (i != start_offset) 191 { 192 igSameLine(0.0f, 0.0f); 193 } 194 ubyte c = state.buffer[i]; 195 if ((c < 32) || (c > 127)) 196 { 197 c = '.'; 198 } 199 igText("%c", c); 200 } 201 } 202 igText("EOF\n"); 203 ImGuiListClipper_End(clipper); 204 ImGuiListClipper_destroy(clipper); 205 igEndChild(); 206 } 207 208 version (Emscripten) 209 { 210 // the async-loading callback for sapp_html5_fetch_dropped_file 211 extern (C) void emsc_load_callback(const (sapp.Html5FetchResponse*) response) @nogc nothrow 212 { 213 if (response.succeeded) 214 { 215 state.load_state = LoadState.Success; 216 state.size = cast(uint) response.data.size; 217 } 218 else if (response.error_code == sapp.Html5FetchError.Fetch_error_buffer_too_small) 219 { 220 state.load_state = LoadState.FileTooBig; 221 } 222 else 223 { 224 state.load_state = LoadState.Failed; 225 } 226 } 227 } 228 else 229 { 230 // the async-loading callback for native platforms 231 extern (C) void native_load_callback(const (sfetch.Response*) response) @nogc nothrow 232 { 233 if (response.fetched) 234 { 235 state.load_state = LoadState.Success; 236 state.size = cast(uint) response.data.size; 237 } 238 else if (response.error_code == sfetch.Error.Buffer_too_small) 239 { 240 state.load_state = LoadState.FileTooBig; 241 } 242 else 243 { 244 state.load_state = LoadState.Failed; 245 } 246 } 247 } 248 249 // -- CIMGUI -- // 250 @nogc nothrow: 251 extern (C) bool igBeginChild_Str(scope const(char)* str_id, const ImVec2 size, bool border, ImGuiWindowFlags flags); 252 extern (C) bool igBegin(const(char)* name, scope bool* p_open, ImGuiWindowFlags flags); 253 extern (C) void igEnd(); 254 extern (C) void igSetNextWindowPos(const ImVec2 pos, ImGuiCond cond, const ImVec2 pivot); 255 extern (C) void igSetNextWindowSize(const ImVec2 size, ImGuiCond cond); 256 extern (C) void igText(scope const(char)* fmt, ...); 257 extern (C) void igSameLine(float offset_from_start_x, float spacing); 258 extern (C) scope ImGuiListClipper* ImGuiListClipper_ImGuiListClipper(); 259 extern (C) void ImGuiListClipper_destroy(scope ImGuiListClipper* self); 260 extern (C) void ImGuiListClipper_Begin(scope ImGuiListClipper* self, int items_count, float items_height); 261 extern (C) void ImGuiListClipper_End(scope ImGuiListClipper* self); 262 extern (C) void igEndChild(); 263 extern (C) void igSeparator(); 264 extern (C) float igGetTextLineHeight(); 265 extern (C) bool ImGuiListClipper_Step(scope ImGuiListClipper* self); 266 267 extern (C): 268 struct ImVec2 269 { 270 float x = 0.0f; 271 float y = 0.0f; 272 } 273 274 enum ImGuiColorEditFlags_ 275 { 276 ImGuiColorEditFlags_None = 0, 277 ImGuiColorEditFlags_NoAlpha = 1 << 1, 278 ImGuiColorEditFlags_NoPicker = 1 << 2, 279 ImGuiColorEditFlags_NoOptions = 1 << 3, 280 ImGuiColorEditFlags_NoSmallPreview = 1 << 4, 281 ImGuiColorEditFlags_NoInputs = 1 << 5, 282 ImGuiColorEditFlags_NoTooltip = 1 << 6, 283 ImGuiColorEditFlags_NoLabel = 1 << 7, 284 ImGuiColorEditFlags_NoSidePreview = 1 << 8, 285 ImGuiColorEditFlags_NoDragDrop = 1 << 9, 286 ImGuiColorEditFlags_NoBorder = 1 << 10, 287 ImGuiColorEditFlags_AlphaBar = 1 << 16, 288 ImGuiColorEditFlags_AlphaPreview = 1 << 17, 289 ImGuiColorEditFlags_AlphaPreviewHalf = 1 << 18, 290 ImGuiColorEditFlags_HDR = 1 << 19, 291 ImGuiColorEditFlags_DisplayRGB = 1 << 20, 292 ImGuiColorEditFlags_DisplayHSV = 1 << 21, 293 ImGuiColorEditFlags_DisplayHex = 1 << 22, 294 ImGuiColorEditFlags_Uint8 = 1 << 23, 295 ImGuiColorEditFlags_Float = 1 << 24, 296 ImGuiColorEditFlags_PickerHueBar = 1 << 25, 297 ImGuiColorEditFlags_PickerHueWheel = 1 << 26, 298 ImGuiColorEditFlags_InputRGB = 1 << 27, 299 ImGuiColorEditFlags_InputHSV = 1 << 28, 300 ImGuiColorEditFlags_DefaultOptions_ 301 = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB 302 | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar, 303 ImGuiColorEditFlags_DisplayMask_ = ImGuiColorEditFlags_DisplayRGB 304 | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex, 305 ImGuiColorEditFlags_DataTypeMask_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float, 306 ImGuiColorEditFlags_PickerMask_ 307 = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar, 308 ImGuiColorEditFlags_InputMask_ = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV 309 } 310 311 enum ImGuiWindowFlags_ 312 { 313 ImGuiWindowFlags_None = 0, 314 ImGuiWindowFlags_NoTitleBar = 1 << 0, 315 ImGuiWindowFlags_NoResize = 1 << 1, 316 ImGuiWindowFlags_NoMove = 1 << 2, 317 ImGuiWindowFlags_NoScrollbar = 1 << 3, 318 ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, 319 ImGuiWindowFlags_NoCollapse = 1 << 5, 320 ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, 321 ImGuiWindowFlags_NoBackground = 1 << 7, 322 ImGuiWindowFlags_NoSavedSettings = 1 << 8, 323 ImGuiWindowFlags_NoMouseInputs = 1 << 9, 324 ImGuiWindowFlags_MenuBar = 1 << 10, 325 ImGuiWindowFlags_HorizontalScrollbar = 1 << 11, 326 ImGuiWindowFlags_NoFocusOnAppearing = 1 << 12, 327 ImGuiWindowFlags_NoBringToFrontOnFocus = 1 << 13, 328 ImGuiWindowFlags_AlwaysVerticalScrollbar = 1 << 14, 329 ImGuiWindowFlags_AlwaysHorizontalScrollbar = 1 << 15, 330 ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, 331 ImGuiWindowFlags_NoNavInputs = 1 << 18, 332 ImGuiWindowFlags_NoNavFocus = 1 << 19, 333 ImGuiWindowFlags_UnsavedDocument = 1 << 20, 334 ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, 335 ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar 336 | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse, 337 ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs 338 | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, 339 ImGuiWindowFlags_NavFlattened = 1 << 23, 340 ImGuiWindowFlags_ChildWindow = 1 << 24, 341 ImGuiWindowFlags_Tooltip = 1 << 25, 342 ImGuiWindowFlags_Popup = 1 << 26, 343 ImGuiWindowFlags_Modal = 1 << 27, 344 ImGuiWindowFlags_ChildMenu = 1 << 28 345 } 346 347 enum ImGuiCond_ 348 { 349 ImGuiCond_None = 0, 350 ImGuiCond_Always = 1 << 0, 351 ImGuiCond_Once = 1 << 1, 352 ImGuiCond_FirstUseEver = 1 << 2, 353 ImGuiCond_Appearing = 1 << 3 354 } 355 356 alias ImGuiCond = int; 357 alias ImGuiColorEditFlags = int; 358 alias ImGuiWindowFlags = int; 359 struct ImGuiListClipper 360 { 361 ImGuiContext* Ctx = null; 362 int DisplayStart = 0; 363 int DisplayEnd = 0; 364 int ItemsCount = 0; 365 float ItemsHeight = 0.0f; 366 float StartPosY = 0.0f; 367 void* TempData = null; 368 } 369 370 struct ImGuiContext;