diff --git a/docs/developer-guide/manifest.md b/docs/developer-guide/manifest.md index 6a9929c..b2d3b61 100644 --- a/docs/developer-guide/manifest.md +++ b/docs/developer-guide/manifest.md @@ -76,7 +76,7 @@ Optional `quickPage` points to a payload bundle. `windows` defaults to an empty | Field | Meaning | | --- | --- | | `entry` / `title` | Required payload entry and nonempty title ≤120 bytes | -| `dockIcon` | false by default; true creates a Dock window | +| `dockIcon` | false by default; controls Dock icon visibility only. Both values use the same SteamVR window controls | | `width` / `height` | Default 1600×900; width 640–2560, height 360–1440 | | `widthMeters` | Default 3.0; finite 0.4–4.0 meters. Source null is accepted but omitted during packaging, then reads back as default 3.0 | | `localWeb` | false by default; specialized localhost windows obtain a URL from backend `window.get` | diff --git a/docs/user-guide/basic-usage.md b/docs/user-guide/basic-usage.md index e4f30dd..884c702 100644 --- a/docs/user-guide/basic-usage.md +++ b/docs/user-guide/basic-usage.md @@ -19,7 +19,7 @@ The manager opens a separate large window with five sections: Plugin, APK, Termi Enable, disable or favorite plugins in the list. Enabled means available for use; backends normally start on demand. Only plugins declaring `autostart` remain resident after startup. Closing a plugin window generally does not disable its backend. Dependency or conflict problems are explained, and affected plugins are shown before changes. -Dock windows use SteamVR close controls. Temporary windows may be destroyed when switching views. Individual plugin interfaces and behavior are maintained by their developers. +Plugin windows use the same SteamVR move, resize, keyboard and close controls, with or without a Dock icon. Individual plugin interfaces and behavior are maintained by their developers. ## Settings and diagnostics diff --git a/docs/zh-CN/developer-guide/manifest.md b/docs/zh-CN/developer-guide/manifest.md index 78046f0..185ac25 100644 --- a/docs/zh-CN/developer-guide/manifest.md +++ b/docs/zh-CN/developer-guide/manifest.md @@ -76,7 +76,7 @@ | 字段 | 说明 | | --- | --- | | `entry` / `title` | 必填;入口需在 payload;标题非空且最多 120 字节 | -| `dockIcon` | 默认 false,true 为独立 Dock 窗口 | +| `dockIcon` | 默认 false,仅控制是否显示 Dock 图标;两种窗口均使用相同的 SteamVR 窗口控件 | | `width` / `height` | 默认 1600×900;宽 640–2560,高 360–1440 | | `widthMeters` | 默认 3.0;有效值 0.4–4.0 米。源码可读取 null,但打包会省略,再读取采用默认 3.0 | | `localWeb` | 默认 false;特殊本机网页窗口由后端 `window.get` 提供 localhost URL | diff --git a/docs/zh-CN/user-guide/basic-usage.md b/docs/zh-CN/user-guide/basic-usage.md index 93ef661..2873050 100644 --- a/docs/zh-CN/user-guide/basic-usage.md +++ b/docs/zh-CN/user-guide/basic-usage.md @@ -19,7 +19,7 @@ 在插件列表启用、停用或收藏插件。启用表示允许使用;默认后端按需启动,只有声明 `autostart` 的插件会常驻。关闭插件窗口通常不会停用后台功能。依赖不满足或有冲突时,界面会给出原因,并在变更前显示受影响插件。 -带 Dock 图标的独立窗口由 SteamVR 提供关闭控件;临时窗口可能在切换界面时销毁。不同插件的 UI 和功能由各自开发者决定。 +无论是否带 Dock 图标,插件窗口都使用相同的 SteamVR 移动、缩放、键盘和关闭控件。不同插件的 UI 和功能由各自开发者决定。 ## 设置与诊断 diff --git a/native/host.cpp b/native/host.cpp index 9bb784e..fb3ec5b 100644 --- a/native/host.cpp +++ b/native/host.cpp @@ -283,16 +283,18 @@ static void anchor_notification(View& v){ v.placement.curve(overlays,v.overlay,toast.width,toast.radius); } static View& create_view(const std::string& key,const std::string& title,const std::string& url,bool dock,int width,int height,const std::string& plugin="",float physical_width=0){ - if(views.count(key)){auto& v=*views.at(key);if(dock&&key!="launcher")overlays->ShowDashboard(key.c_str());else if(key!="notifications"&&key!="launcher")show(v,true);return v;} + // Plugin windows always use Steam window controls; dock only controls the tab. + const bool dashboard=dock||!plugin.empty(); + if(views.count(key)){auto& v=*views.at(key);if(v.dashboard&&key!="launcher")overlays->ShowDashboard(key.c_str());else if(key!="notifications"&&key!="launcher")show(v,true);return v;} // Use 1.5x for launcher artwork and 2x for scrolling panels. CEF readback, // texture upload and launcher mipmap generation scale with the pixel count. - if(views.size()>=34)throw std::runtime_error("Window limit exceeded");auto v=std::make_unique();v->key=key;v->plugin=plugin;v->title=title;v->dashboard=dock;v->width=width;v->height=height;v->render_scale=key=="launcher"?1.5f:(key=="framely.manager"||key=="menu"||key=="notifications")?2:1; - auto error=dock?overlays->CreateDashboardOverlay(key.c_str(),title.c_str(),&v->overlay,&v->thumbnail):overlays->CreateOverlay(key.c_str(),title.c_str(),&v->overlay);if(error)throw std::runtime_error("Overlay creation failed: "+std::to_string(error)); + if(views.size()>=34)throw std::runtime_error("Window limit exceeded");auto v=std::make_unique();v->key=key;v->plugin=plugin;v->title=title;v->dashboard=dashboard;v->width=width;v->height=height;v->render_scale=key=="launcher"?1.5f:(key=="framely.manager"||key=="menu"||key=="notifications")?2:1; + auto error=dashboard?overlays->CreateDashboardOverlay(key.c_str(),title.c_str(),&v->overlay,&v->thumbnail):overlays->CreateOverlay(key.c_str(),title.c_str(),&v->overlay);if(error)throw std::runtime_error("Overlay creation failed: "+std::to_string(error)); overlays->SetOverlayInputMethod(v->overlay,key=="notifications"?vr::VROverlayInputMethod_None:vr::VROverlayInputMethod_Mouse);vr::HmdVector2_t scale{{float(width),float(height)}};overlays->SetOverlayMouseScale(v->overlay,&scale);overlays->SetOverlayFlag(v->overlay,vr::VROverlayFlags_HideLaserIntersection,false);overlays->SetOverlayFlag(v->overlay,vr::VROverlayFlags_SendVRSmoothScrollEvents,true);overlays->SetOverlayFlag(v->overlay,vr::VROverlayFlags_VisibleInDashboard,true);overlays->SetOverlayFlag(v->overlay,vr::VROverlayFlags_MakeOverlaysInteractiveIfVisible,true);overlays->SetOverlayFlag(v->overlay,vr::VROverlayFlags_SortWithNonSceneOverlays,false);overlays->SetOverlaySortOrder(v->overlay,0); // Keep the runtime's full dashboard controls, including its keyboard and close buttons. - if(dock){ - if(key=="launcher")overlays->SetOverlayFlag(v->overlay,vr::VROverlayFlags_NoDashboardTab,true); + if(dashboard){ + overlays->SetOverlayFlag(v->overlay,vr::VROverlayFlags_NoDashboardTab,!dock||key=="launcher"); auto result=overlays->SetOverlayFlag(v->overlay,vr::VROverlayFlags_MinimalControlBar,key=="launcher"); if(result)std::cerr<<"Dashboard controls unavailable "<SetOverlayRaw(v->thumbnail,pixels.data(),128,128,4);overlays->SetOverlayWidthInMeters(v->overlay,physical_width>0?physical_width:(key=="framely.manager"?3.2f:key=="launcher"?3.6f:1.2f));}else if(key!="menu"&&key!="notifications"&&key!="launcher"){anchor_to_head(*v,physical_width>0?physical_width:1.0f);} + if(dashboard){auto pixels=icon(false,true);overlays->SetOverlayRaw(v->thumbnail,pixels.data(),128,128,4);overlays->SetOverlayWidthInMeters(v->overlay,physical_width>0?physical_width:(key=="framely.manager"?3.2f:key=="launcher"?3.6f:1.2f));}else if(key!="menu"&&key!="notifications"&&key!="launcher"){anchor_to_head(*v,physical_width>0?physical_width:1.0f);} cef_window_info_t info{};info.size=sizeof(info);info.windowless_rendering_enabled=1;info.runtime_style=CEF_RUNTIME_STYLE_ALLOY;cef_browser_settings_t bs{};bs.size=sizeof(bs);bs.windowless_frame_rate=browser_frame_rate();bs.background_color=0;std::cout<<"Browser frame rate "<DestroyOverlay(v->overlay);if(v->thumbnail)overlays->DestroyOverlay(v->thumbnail);throw std::runtime_error("Browser creation failed");}v->browser=browser;v->browser_id=browser->get_identifier(browser);v->host=browser->get_host(browser);glGenTextures(2,v->textures);auto* ptr=v.get();views.emplace(key,std::move(v));ptr->host->notify_screen_info_changed(ptr->host);ptr->host->was_resized(ptr->host);ptr->host->was_hidden(ptr->host,key!="notifications");if(dock&&key!="launcher")overlays->ShowDashboard(key.c_str());else if(key!="menu"&&key!="notifications"&&key!="launcher")show(*ptr,true);return *ptr; + if(!browser){overlays->DestroyOverlay(v->overlay);if(v->thumbnail)overlays->DestroyOverlay(v->thumbnail);throw std::runtime_error("Browser creation failed");}v->browser=browser;v->browser_id=browser->get_identifier(browser);v->host=browser->get_host(browser);glGenTextures(2,v->textures);auto* ptr=v.get();views.emplace(key,std::move(v));ptr->host->notify_screen_info_changed(ptr->host);ptr->host->was_resized(ptr->host);ptr->host->was_hidden(ptr->host,key!="notifications");if(ptr->dashboard&&key!="launcher")overlays->ShowDashboard(key.c_str());else if(key!="menu"&&key!="notifications"&&key!="launcher")show(*ptr,true);return *ptr; } static void plugin_thumbnail(View& view,const std::string& encoded){ if(!view.thumbnail||encoded.empty()||encoded.size()>1400000)return;