33#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
34UIEdgeInsets GetSafeAreaInsets(MTKView* view) {
36 return UIEdgeInsetsZero;
38 if (@available(iOS 11.0, *)) {
39 return view.safeAreaInsets;
41 return UIEdgeInsetsZero;
47void ApplyTouchStyle(MTKView* view) {
48 struct TouchStyleState {
49 bool initialized =
false;
50 float last_scale = 0.0f;
51 float last_safe_x = -1.0f;
52 float last_safe_y = -1.0f;
54 ImVec2 touch_extra = ImVec2(0.0f, 0.0f);
55 ImVec2 frame_padding = ImVec2(0.0f, 0.0f);
56 ImVec2 item_spacing = ImVec2(0.0f, 0.0f);
57 float scrollbar_size = 0.0f;
58 float grab_min_size = 0.0f;
61 static TouchStyleState state;
63 ImGuiStyle& style = ImGui::GetStyle();
64 ImGuiIO& io = ImGui::GetIO();
65 io.ConfigWindowsMoveFromTitleBarOnly =
true;
66 io.ConfigWindowsResizeFromEdges =
false;
68 if (!state.initialized) {
69 state.touch_extra = style.TouchExtraPadding;
70 state.frame_padding = style.FramePadding;
71 state.item_spacing = style.ItemSpacing;
72 state.scrollbar_size = style.ScrollbarSize;
73 state.grab_min_size = style.GrabMinSize;
74 state.initialized =
true;
80 if (scale != state.last_scale) {
81 state.last_scale = scale;
83 const float frame_height = ImGui::GetFrameHeight();
84 const float target_height = std::max(44.0f * scale, frame_height);
85 const float touch_extra =
86 std::clamp((target_height - frame_height) * 0.5f, 0.0f, 16.0f * scale);
87 style.TouchExtraPadding =
88 ImVec2(std::max(state.touch_extra.x * scale, touch_extra),
89 std::max(state.touch_extra.y * scale, touch_extra));
91 const float font_size = ImGui::GetFontSize();
92 if (font_size > 0.0f) {
93 style.ScrollbarSize = std::max(state.scrollbar_size * scale,
94 font_size * 1.1f * scale);
96 std::max(state.grab_min_size * scale, font_size * 0.9f * scale);
97 style.FramePadding.x = std::max(state.frame_padding.x * scale,
98 font_size * 0.55f * scale);
99 style.FramePadding.y = std::max(state.frame_padding.y * scale,
100 font_size * 0.35f * scale);
101 style.ItemSpacing.x = std::max(state.item_spacing.x * scale,
102 font_size * 0.45f * scale);
103 style.ItemSpacing.y = std::max(state.item_spacing.y * scale,
104 font_size * 0.35f * scale);
108 style.WindowRounding = 8.0f * scale;
109 style.PopupRounding = 6.0f * scale;
110 style.TabRounding = 4.0f * scale;
111 style.ScrollbarRounding = 6.0f * scale;
112 style.TabCloseButtonMinWidthUnselected = 44.0f * scale;
113 style.WindowMinSize = ImVec2(200.0f * scale, 150.0f * scale);
118 const UIEdgeInsets insets = GetSafeAreaInsets(view);
119 const float safe_x = std::max((
float)insets.left, (
float)insets.right);
120 const float safe_y = std::max((
float)insets.top, (
float)insets.bottom);
123 const float stable_x = std::floor(safe_x);
124 const float stable_y = std::floor(safe_y);
126 if (stable_x != state.last_safe_x || stable_y != state.last_safe_y) {
127 state.last_safe_x = stable_x;
128 state.last_safe_y = stable_y;
129 style.DisplaySafeAreaPadding = ImVec2(stable_x, stable_y);
298 return absl::OkStatus();
302 return absl::InvalidArgumentError(
"Renderer is null");
305#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
307 return absl::FailedPreconditionError(
"Metal view not set");
311 id<MTLDevice> device = view.device;
313 device = MTLCreateSystemDefaultDevice();
314 view.device = device;
318 return absl::InternalError(
"Failed to create Metal device");
321 IMGUI_CHECKVERSION();
322 ImGui::CreateContext();
324 ImGuiIO& io = ImGui::GetIO();
325 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
326 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
327 io.ConfigFlags |= ImGuiConfigFlags_IsTouchScreen;
330 static std::string ini_path_str;
331 if (ini_path_str.empty()) {
332 ini_path_str = ini_path->string();
334 io.IniFilename = ini_path_str.c_str();
336 io.IniFilename =
nullptr;
337 LOG_WARN(
"IOSWindowBackend",
"Failed to resolve ImGui ini path: %s",
338 ini_path.status().ToString().c_str());
341 if (!ImGui_ImplMetal_Init(device)) {
342 return absl::InternalError(
"ImGui_ImplMetal_Init failed");
346 if (!font_status.ok()) {
347 ImGui_ImplMetal_Shutdown();
348 ImGui::DestroyContext();
352#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
353 ApplyTouchStyle(view);
357 id<MTLCommandQueue> queue = [device newCommandQueue];
362 LOG_INFO(
"IOSWindowBackend",
"ImGui initialized with Metal backend");
363 return absl::OkStatus();
365 return absl::FailedPreconditionError(
366 "IOSWindowBackend is only available on iOS");
409#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
411 ImGuiContext* ctx = ImGui::GetCurrentContext();
412 const ImGuiViewport* vp = ImGui::GetMainViewport();
415 const float safe_left = std::max(0.0f, insets.left);
416 const float safe_right = std::max(0.0f, insets.right);
417 const float safe_bottom = std::max(0.0f, insets.bottom);
420 const float safe_top =
423 const ImVec2 rect_pos(vp->WorkPos.x + safe_left, vp->WorkPos.y + safe_top);
424 const ImVec2 rect_size(
425 std::max(1.0f, vp->WorkSize.x - safe_left - safe_right),
426 std::max(1.0f, vp->WorkSize.y - safe_top - safe_bottom));
428 for (ImGuiWindow* win : ctx->Windows) {
429 if (!win || win->Hidden || win->IsFallbackWindow)
continue;
430 if (win->DockIsActive || win->DockNodeAsHost)
continue;
431 if (win->Flags & ImGuiWindowFlags_NoMove)
continue;
432 if (win->Flags & ImGuiWindowFlags_ChildWindow)
continue;
435 win->Pos, win->Size, rect_pos, rect_size, 48.0f);
436 if (result.clamped) {
437 ImGui::SetWindowPos(win, result.pos, ImGuiCond_Always);
446#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
449 if (!view || !view.currentDrawable) {
453 auto* render_pass = view.currentRenderPassDescriptor;
458 id<MTLCommandQueue> queue =
460 id<MTLCommandBuffer> command_buffer = [queue commandBuffer];
461 id<MTLRenderCommandEncoder> encoder =
462 [command_buffer renderCommandEncoderWithDescriptor:render_pass];
464 ImGui_ImplMetal_RenderDrawData(ImGui::GetDrawData(), command_buffer, encoder);
465 [encoder endEncoding];
466 [command_buffer presentDrawable:view.currentDrawable];
467 [command_buffer commit];