34#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
35UIEdgeInsets GetSafeAreaInsets(MTKView* view) {
37 return UIEdgeInsetsZero;
39 if (@available(iOS 11.0, *)) {
40 return view.safeAreaInsets;
42 return UIEdgeInsetsZero;
48void ApplyTouchStyle(MTKView* view) {
49 struct TouchStyleState {
50 bool initialized =
false;
51 float last_scale = 0.0f;
52 float last_safe_x = -1.0f;
53 float last_safe_y = -1.0f;
55 ImVec2 touch_extra = ImVec2(0.0f, 0.0f);
56 ImVec2 frame_padding = ImVec2(0.0f, 0.0f);
57 ImVec2 item_spacing = ImVec2(0.0f, 0.0f);
58 float scrollbar_size = 0.0f;
59 float grab_min_size = 0.0f;
62 static TouchStyleState state;
64 ImGuiStyle& style = ImGui::GetStyle();
65 ImGuiIO& io = ImGui::GetIO();
66 io.ConfigWindowsMoveFromTitleBarOnly =
true;
67 io.ConfigWindowsResizeFromEdges =
false;
69 if (!state.initialized) {
70 state.touch_extra = style.TouchExtraPadding;
71 state.frame_padding = style.FramePadding;
72 state.item_spacing = style.ItemSpacing;
73 state.scrollbar_size = style.ScrollbarSize;
74 state.grab_min_size = style.GrabMinSize;
75 state.initialized =
true;
81 if (scale != state.last_scale) {
82 state.last_scale = scale;
84 const float frame_height = ImGui::GetFrameHeight();
85 const float target_height = std::max(44.0f * scale, frame_height);
86 const float touch_extra =
87 std::clamp((target_height - frame_height) * 0.5f, 0.0f, 16.0f * scale);
88 style.TouchExtraPadding =
89 ImVec2(std::max(state.touch_extra.x * scale, touch_extra),
90 std::max(state.touch_extra.y * scale, touch_extra));
92 const float font_size = ImGui::GetFontSize();
93 if (font_size > 0.0f) {
94 style.ScrollbarSize = std::max(state.scrollbar_size * scale,
95 font_size * 1.1f * scale);
97 std::max(state.grab_min_size * scale, font_size * 0.9f * scale);
98 style.FramePadding.x = std::max(state.frame_padding.x * scale,
99 font_size * 0.55f * scale);
100 style.FramePadding.y = std::max(state.frame_padding.y * scale,
101 font_size * 0.35f * scale);
102 style.ItemSpacing.x = std::max(state.item_spacing.x * scale,
103 font_size * 0.45f * scale);
104 style.ItemSpacing.y = std::max(state.item_spacing.y * scale,
105 font_size * 0.35f * scale);
109 style.WindowRounding = 8.0f * scale;
110 style.PopupRounding = 6.0f * scale;
111 style.TabRounding = 4.0f * scale;
112 style.ScrollbarRounding = 6.0f * scale;
113 style.TabCloseButtonMinWidthUnselected = 44.0f * scale;
114 style.WindowMinSize = ImVec2(200.0f * scale, 150.0f * scale);
119 const UIEdgeInsets insets = GetSafeAreaInsets(view);
120 const float safe_x = std::max((
float)insets.left, (
float)insets.right);
121 const float safe_y = std::max((
float)insets.top, (
float)insets.bottom);
124 const float stable_x = std::floor(safe_x);
125 const float stable_y = std::floor(safe_y);
127 if (stable_x != state.last_safe_x || stable_y != state.last_safe_y) {
128 state.last_safe_x = stable_x;
129 state.last_safe_y = stable_y;
130 style.DisplaySafeAreaPadding = ImVec2(stable_x, stable_y);
299 return absl::OkStatus();
303 return absl::InvalidArgumentError(
"Renderer is null");
306#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
308 return absl::FailedPreconditionError(
"Metal view not set");
312 id<MTLDevice> device = view.device;
314 device = MTLCreateSystemDefaultDevice();
315 view.device = device;
319 return absl::InternalError(
"Failed to create Metal device");
322 IMGUI_CHECKVERSION();
323 ImGui::CreateContext();
325 ImGuiIO& io = ImGui::GetIO();
327 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
328 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
329 io.ConfigFlags |= ImGuiConfigFlags_IsTouchScreen;
332 static std::string ini_path_str;
333 if (ini_path_str.empty()) {
334 ini_path_str = ini_path->string();
336 io.IniFilename = ini_path_str.c_str();
338 io.IniFilename =
nullptr;
339 LOG_WARN(
"IOSWindowBackend",
"Failed to resolve ImGui ini path: %s",
340 ini_path.status().ToString().c_str());
343 if (!ImGui_ImplMetal_Init(device)) {
344 return absl::InternalError(
"ImGui_ImplMetal_Init failed");
348 if (!font_status.ok()) {
349 ImGui_ImplMetal_Shutdown();
350 ImGui::DestroyContext();
354#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
355 ApplyTouchStyle(view);
359 id<MTLCommandQueue> queue = [device newCommandQueue];
364 LOG_INFO(
"IOSWindowBackend",
"ImGui initialized with Metal backend");
365 return absl::OkStatus();
367 return absl::FailedPreconditionError(
368 "IOSWindowBackend is only available on iOS");
411#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
413 ImGuiContext* ctx = ImGui::GetCurrentContext();
414 const ImGuiViewport* vp = ImGui::GetMainViewport();
417 const float safe_left = std::max(0.0f, insets.left);
418 const float safe_right = std::max(0.0f, insets.right);
419 const float safe_bottom = std::max(0.0f, insets.bottom);
422 const float safe_top =
425 const ImVec2 rect_pos(vp->WorkPos.x + safe_left, vp->WorkPos.y + safe_top);
426 const ImVec2 rect_size(
427 std::max(1.0f, vp->WorkSize.x - safe_left - safe_right),
428 std::max(1.0f, vp->WorkSize.y - safe_top - safe_bottom));
430 for (ImGuiWindow* win : ctx->Windows) {
431 if (!win || win->Hidden || win->IsFallbackWindow)
continue;
432 if (win->DockIsActive || win->DockNodeAsHost)
continue;
433 if (win->Flags & ImGuiWindowFlags_NoMove)
continue;
434 if (win->Flags & ImGuiWindowFlags_ChildWindow)
continue;
437 win->Pos, win->Size, rect_pos, rect_size, 48.0f);
438 if (result.clamped) {
439 ImGui::SetWindowPos(win, result.pos, ImGuiCond_Always);
448#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
451 if (!view || !view.currentDrawable) {
455 auto* render_pass = view.currentRenderPassDescriptor;
460 id<MTLCommandQueue> queue =
462 id<MTLCommandBuffer> command_buffer = [queue commandBuffer];
463 id<MTLRenderCommandEncoder> encoder =
464 [command_buffer renderCommandEncoderWithDescriptor:render_pass];
466 ImGui_ImplMetal_RenderDrawData(ImGui::GetDrawData(), command_buffer, encoder);
467 [encoder endEncoding];
468 [command_buffer presentDrawable:view.currentDrawable];
469 [command_buffer commit];