277 lines
9.9 KiB
C++
277 lines
9.9 KiB
C++
#include "webview_utils.h"
|
|
#include "defines/globals.h"
|
|
#include "fmt/xchar.h"
|
|
#include "global/globals.h"
|
|
#include "log/candidate_diag_log.h"
|
|
#include "spdlog/spdlog.h"
|
|
#include "utils/common_utils.h"
|
|
#include <boost/json.hpp>
|
|
|
|
#undef DIAG_LOGF
|
|
#define DIAG_LOGF(...) ((void)0)
|
|
|
|
namespace json = boost::json;
|
|
|
|
void MeasureDomUpdateTime(ComPtr<ICoreWebView2> webview)
|
|
{
|
|
std::wstring script =
|
|
LR"(document.body.innerHTML = '<div>1. 原来</div> <div>2. 如此</div> <div>3. 竟然</div> <div>4. 这样</div> <div>5. 可恶</div> <div>6. 棋盘</div> <div>7. 磨合</div> <div>8. 樱花</div> </body>';)";
|
|
|
|
auto start = std::chrono::high_resolution_clock::now();
|
|
|
|
webview->ExecuteScript(script.c_str(), nullptr);
|
|
|
|
auto end = std::chrono::high_resolution_clock::now();
|
|
|
|
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
|
std::wstring message = L"DOM update time: " + std::to_wstring(duration.count()) + L" μs";
|
|
(void)0;
|
|
}
|
|
|
|
std::pair<double, double> ParseDivSize(const std::wstring &jsonResult)
|
|
{
|
|
std::string jsonStr = wstring_to_string(jsonResult);
|
|
auto size =
|
|
std::make_pair(static_cast<double>(::CANDIDATE_WINDOW_WIDTH), static_cast<double>(::CANDIDATE_WINDOW_HEIGHT));
|
|
try
|
|
{
|
|
json::value parsed = json::parse(jsonStr);
|
|
if (parsed.is_string())
|
|
{
|
|
parsed = json::parse(parsed.as_string());
|
|
}
|
|
double width = parsed.at("width").is_int64() //
|
|
? parsed.at("width").as_int64() //
|
|
: parsed.at("width").as_double(); //
|
|
double height = parsed.at("height").is_int64() //
|
|
? parsed.at("height").as_int64() //
|
|
: parsed.at("height").as_double(); //
|
|
size = std::make_pair(width, height);
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
(void)0;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
void GetCandidateCardSize(
|
|
ComPtr<ICoreWebView2> webview,
|
|
const wchar_t *boxId,
|
|
const wchar_t *parentId,
|
|
double maxWidthDip,
|
|
double maxHeightDip,
|
|
std::function<void(std::pair<double, double>)> callback)
|
|
{
|
|
if (!webview)
|
|
{
|
|
DIAG_LOGF(L"ui-measure box={} parent={} skipped: no webview", boxId, parentId);
|
|
callback({0.0, 0.0});
|
|
return;
|
|
}
|
|
if (maxWidthDip < 1.0)
|
|
{
|
|
maxWidthDip = static_cast<double>(::CANDIDATE_WINDOW_MAX_WIDTH_DIP);
|
|
}
|
|
if (maxHeightDip < 1.0)
|
|
{
|
|
// Same order of magnitude as the stable quarter-screen host height.
|
|
maxHeightDip = static_cast<double>(::CANDIDATE_WINDOW_MAX_WIDTH_DIP);
|
|
}
|
|
std::wstring script = fmt::format(
|
|
LR"(
|
|
(function() {{
|
|
var box = document.getElementById("{0}");
|
|
var el = document.getElementById("{1}");
|
|
var target = box || el;
|
|
if (!target) {{
|
|
return JSON.stringify({{width: 0, height: 0}});
|
|
}}
|
|
var maxW = {2};
|
|
var maxH = {3};
|
|
if (box) {{
|
|
box.style.maxWidth = maxW + "px";
|
|
box.style.maxHeight = maxH + "px";
|
|
box.style.width = "fit-content";
|
|
box.style.boxSizing = "border-box";
|
|
box.style.whiteSpace = "nowrap";
|
|
box.style.overflowX = "hidden";
|
|
box.style.overflowY = "auto";
|
|
}}
|
|
if (el) {{
|
|
el.style.maxWidth = maxW + "px";
|
|
el.style.width = "fit-content";
|
|
}}
|
|
void target.offsetWidth;
|
|
void target.offsetHeight;
|
|
var rect = target.getBoundingClientRect();
|
|
// scrollWidth catches glyph overflow that rect/offsetWidth miss at
|
|
// high DPI; +1 DIP avoids ceil→floor undersize that clips the last
|
|
// candidate against SetWindowRgn / screen edges.
|
|
var width = Math.min(
|
|
maxW,
|
|
Math.max(rect.width, target.offsetWidth || 0, target.scrollWidth || 0) + 1);
|
|
// Visible (capped) height for host region — NOT full scrollHeight.
|
|
var height = Math.min(
|
|
maxH,
|
|
Math.max(rect.height, target.offsetHeight || 0) + 1);
|
|
return JSON.stringify({{width: width, height: height}});
|
|
}})();
|
|
)",
|
|
boxId, parentId, maxWidthDip, maxHeightDip);
|
|
const HRESULT submitHr = webview->ExecuteScript(
|
|
script.c_str(),
|
|
Callback<ICoreWebView2ExecuteScriptCompletedHandler>([callback, boxId = std::wstring(boxId),
|
|
parentId = std::wstring(parentId), maxWidthDip,
|
|
maxHeightDip](HRESULT errorCode,
|
|
LPCWSTR result) -> HRESULT {
|
|
std::pair<double, double> size{};
|
|
if (SUCCEEDED(errorCode) && result)
|
|
{
|
|
size = ParseDivSize(result);
|
|
}
|
|
DIAG_LOGF(L"ui-measure box={} parent={} max_dip=({:.1f},{:.1f}) callback_hr={:#x} "
|
|
L"result_chars={} measured_dip=({:.2f},{:.2f})",
|
|
boxId, parentId, maxWidthDip, maxHeightDip, static_cast<unsigned>(errorCode),
|
|
result ? wcslen(result) : 0, size.first, size.second);
|
|
callback(size);
|
|
return S_OK;
|
|
}).Get());
|
|
DIAG_LOGF(L"ui-measure submit box={} parent={} max_dip=({:.1f},{:.1f}) hr={:#x}", boxId, parentId,
|
|
maxWidthDip, maxHeightDip, static_cast<unsigned>(submitHr));
|
|
if (FAILED(submitHr))
|
|
{
|
|
callback({0.0, 0.0});
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
void GetContainerSizeCand(ComPtr<ICoreWebView2> webview, std::function<void(std::pair<double, double>)> callback,
|
|
double maxWidthDip, double maxHeightDip)
|
|
{
|
|
GetCandidateCardSize(webview, L"measureContainer", L"measureContainerParent", maxWidthDip, maxHeightDip,
|
|
std::move(callback));
|
|
}
|
|
|
|
void GetRealCandidateCardSize(ComPtr<ICoreWebView2> webview, std::function<void(std::pair<double, double>)> callback,
|
|
double maxWidthDip, double maxHeightDip)
|
|
{
|
|
GetCandidateCardSize(webview, L"realContainer", L"realContainerParent", maxWidthDip, maxHeightDip,
|
|
std::move(callback));
|
|
}
|
|
|
|
void GetContainerSizeFtb(ComPtr<ICoreWebView2> webview, std::function<void(std::pair<double, double>)> callback)
|
|
{
|
|
if (!webview)
|
|
{
|
|
callback({0.0, 0.0});
|
|
return;
|
|
}
|
|
const wchar_t *script =
|
|
LR"((function() {
|
|
var el = document.querySelector('.status-bar');
|
|
if (!el) {
|
|
return JSON.stringify({width: 0, height: 0});
|
|
}
|
|
void el.offsetWidth;
|
|
var rect = el.getBoundingClientRect();
|
|
var width = Math.max(rect.width, el.offsetWidth || 0, el.scrollWidth || 0);
|
|
var height = Math.max(rect.height, el.offsetHeight || 0, el.scrollHeight || 0);
|
|
return JSON.stringify({width: width, height: height});
|
|
})();)";
|
|
const HRESULT submitHr = webview->ExecuteScript(
|
|
script,
|
|
Callback<ICoreWebView2ExecuteScriptCompletedHandler>([callback](HRESULT errorCode, LPCWSTR result) -> HRESULT {
|
|
std::pair<double, double> size{};
|
|
if (SUCCEEDED(errorCode) && result)
|
|
{
|
|
size = ParseDivSize(result);
|
|
}
|
|
callback(size);
|
|
return S_OK;
|
|
}).Get());
|
|
(void)submitHr;
|
|
}
|
|
|
|
void GetContainerSizeMenu(ComPtr<ICoreWebView2> webview, std::function<void(std::pair<double, double>)> callback)
|
|
{
|
|
std::wstring script = LR"(
|
|
(function() {
|
|
document.documentElement.style.overflow = "hidden";
|
|
document.body.style.overflow = "hidden";
|
|
var menu = document.getElementById("menuContainer");
|
|
var rect = menu.getBoundingClientRect();
|
|
return JSON.stringify({
|
|
width: Math.max(rect.width, menu.offsetWidth || 0, menu.scrollWidth || 0),
|
|
height: Math.max(rect.height, menu.offsetHeight || 0, menu.scrollHeight || 0)
|
|
});
|
|
})();
|
|
)";
|
|
webview->ExecuteScript( //
|
|
script.c_str(), //
|
|
Callback<ICoreWebView2ExecuteScriptCompletedHandler>([callback](HRESULT errorCode, LPCWSTR result) -> HRESULT {
|
|
std::pair<double, double> size;
|
|
if (SUCCEEDED(errorCode) && result)
|
|
{
|
|
size = ParseDivSize(result);
|
|
// (void)0;
|
|
}
|
|
else
|
|
{
|
|
}
|
|
callback(size);
|
|
return S_OK;
|
|
}).Get());
|
|
}
|
|
|
|
void MoveContainerBottom(ComPtr<ICoreWebView2> webview, int marginTop)
|
|
{
|
|
MoveContainerBottom(webview, marginTop, nullptr);
|
|
}
|
|
|
|
void MoveContainerBottom(ComPtr<ICoreWebView2> webview, int marginTop, std::function<void()> onComplete)
|
|
{
|
|
if (!webview)
|
|
{
|
|
if (onComplete)
|
|
{
|
|
onComplete();
|
|
}
|
|
return;
|
|
}
|
|
std::wstring script;
|
|
script.reserve(256);
|
|
script.append(L"var el = document.getElementById('realContainerParent');");
|
|
script.append(L"if (el) {");
|
|
script.append(L"el.style.marginTop = '");
|
|
script.append(std::to_wstring(marginTop));
|
|
script.append(L"px';");
|
|
script.append(L"el.style.marginLeft = '");
|
|
script.append(std::to_wstring(Global::MarginLeft));
|
|
script.append(L"px';");
|
|
script.append(L"}");
|
|
if (!onComplete)
|
|
{
|
|
webview->ExecuteScript(script.c_str(), nullptr);
|
|
return;
|
|
}
|
|
webview->ExecuteScript(
|
|
script.c_str(),
|
|
Callback<ICoreWebView2ExecuteScriptCompletedHandler>([onComplete](HRESULT, LPCWSTR) -> HRESULT {
|
|
onComplete();
|
|
return S_OK;
|
|
}).Get());
|
|
}
|
|
|
|
void MakeBodyVisible(ComPtr<ICoreWebView2> webview)
|
|
{
|
|
if (!webview)
|
|
{
|
|
return;
|
|
}
|
|
std::wstring script = L"document.body.style.visibility = \"visible\";";
|
|
webview->ExecuteScript(script.c_str(), nullptr);
|
|
}
|