mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2025-07-06 09:15:23 +08:00
qt 6.6.0 clean
This commit is contained in:
36
tests/manual/wasm/qwasmwindow/CMakeLists.txt
Normal file
36
tests/manual/wasm/qwasmwindow/CMakeLists.txt
Normal file
@ -0,0 +1,36 @@
|
||||
qt_internal_add_manual_test(qwasmwindow_harness
|
||||
SOURCES
|
||||
qwasmwindow_harness.cpp
|
||||
LIBRARIES
|
||||
Qt::Core
|
||||
Qt::CorePrivate
|
||||
Qt::GuiPrivate
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
TARGET qwasmwindow_harness POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qwasmwindow_harness.html
|
||||
${CMAKE_CURRENT_BINARY_DIR}/qwasmwindow_harness.html
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
TARGET qwasmwindow_harness POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qwasmwindow.py
|
||||
${CMAKE_CURRENT_BINARY_DIR}/qwasmwindow.py
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
TARGET qwasmwindow_harness POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/run.sh
|
||||
${CMAKE_CURRENT_BINARY_DIR}/run.sh
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
TARGET qwasmwindow_harness POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../util/wasm/qtwasmserver/qtwasmserver.py
|
||||
${CMAKE_CURRENT_BINARY_DIR}/qtwasmserver.py
|
||||
)
|
445
tests/manual/wasm/qwasmwindow/qwasmwindow.py
Normal file
445
tests/manual/wasm/qwasmwindow/qwasmwindow.py
Normal file
@ -0,0 +1,445 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
from selenium.webdriver import Chrome
|
||||
from selenium.webdriver.common.actions.action_builder import ActionBuilder
|
||||
from selenium.webdriver.common.actions.pointer_actions import PointerActions
|
||||
from selenium.webdriver.common.actions.interaction import POINTER_TOUCH
|
||||
from selenium.webdriver.common.actions.pointer_input import PointerInput
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.expected_conditions import presence_of_element_located
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.common.action_chains import ActionChains
|
||||
|
||||
import unittest
|
||||
from enum import Enum, auto
|
||||
|
||||
class WidgetTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self._driver = Chrome()
|
||||
self._driver.get(
|
||||
'http://localhost:8001/qwasmwindow_harness.html')
|
||||
self._test_sandbox_element = WebDriverWait(self._driver, 30).until(
|
||||
presence_of_element_located((By.ID, 'test-sandbox'))
|
||||
)
|
||||
self.addTypeEqualityFunc(Rect, assert_rects_equal)
|
||||
|
||||
def test_window_resizing(self):
|
||||
defaultWindowMinSize = 100
|
||||
screen = Screen(self._driver, ScreenPosition.FIXED,
|
||||
x=0, y=0, width=600, height=600)
|
||||
window = Window(screen, x=100, y=100, width=200, height=200)
|
||||
self.assertEqual(window.rect, Rect(x=100, y=100, width=200, height=200))
|
||||
|
||||
window.drag(Handle.TOP_LEFT, direction=UP(10) + LEFT(10))
|
||||
self.assertEqual(window.rect, Rect(x=90, y=90, width=210, height=210))
|
||||
|
||||
window.drag(Handle.TOP, direction=DOWN(10) + LEFT(100))
|
||||
self.assertEqual(window.rect, Rect(x=90, y=100, width=210, height=200))
|
||||
|
||||
window.drag(Handle.TOP_RIGHT, direction=UP(5) + LEFT(5))
|
||||
self.assertEqual(window.rect, Rect(x=90, y=95, width=205, height=205))
|
||||
|
||||
window.drag(Handle.RIGHT, direction=DOWN(100) + RIGHT(5))
|
||||
self.assertEqual(window.rect, Rect(x=90, y=95, width=210, height=205))
|
||||
|
||||
window.drag(Handle.BOTTOM_RIGHT, direction=UP(5) + LEFT(10))
|
||||
self.assertEqual(window.rect, Rect(x=90, y=95, width=200, height=200))
|
||||
|
||||
window.drag(Handle.BOTTOM, direction=DOWN(20) + LEFT(100))
|
||||
self.assertEqual(window.rect, Rect(x=90, y=95, width=200, height=220))
|
||||
|
||||
window.drag(Handle.BOTTOM_LEFT, direction=DOWN(10) + LEFT(10))
|
||||
self.assertEqual(window.rect, Rect(x=80, y=95, width=210, height=230))
|
||||
|
||||
window.drag(Handle.LEFT, direction=DOWN(343) + LEFT(5))
|
||||
self.assertEqual(window.rect, Rect(x=75, y=95, width=215, height=230))
|
||||
|
||||
window.drag(Handle.BOTTOM_RIGHT, direction=UP(150) + LEFT(150))
|
||||
self.assertEqual(window.rect, Rect(x=75, y=95, width=defaultWindowMinSize, height=defaultWindowMinSize))
|
||||
|
||||
def test_cannot_resize_over_screen_top_edge(self):
|
||||
screen = Screen(self._driver, ScreenPosition.FIXED,
|
||||
x=200, y=200, width=300, height=300)
|
||||
window = Window(screen, x=300, y=300, width=100, height=100)
|
||||
self.assertEqual(window.rect, Rect(x=300, y=300, width=100, height=100))
|
||||
frame_rect_before_resize = window.frame_rect
|
||||
|
||||
window.drag(Handle.TOP, direction=UP(200))
|
||||
self.assertEqual(window.rect.x, 300)
|
||||
self.assertEqual(window.frame_rect.y, screen.rect.y)
|
||||
self.assertEqual(window.rect.width, 100)
|
||||
self.assertEqual(window.frame_rect.y + window.frame_rect.height,
|
||||
frame_rect_before_resize.y + frame_rect_before_resize.height)
|
||||
|
||||
def test_window_move(self):
|
||||
screen = Screen(self._driver, ScreenPosition.FIXED,
|
||||
x=200, y=200, width=300, height=300)
|
||||
window = Window(screen, x=300, y=300, width=100, height=100)
|
||||
self.assertEqual(window.rect, Rect(x=300, y=300, width=100, height=100))
|
||||
|
||||
window.drag(Handle.TOP_WINDOW_BAR, direction=UP(30))
|
||||
self.assertEqual(window.rect, Rect(x=300, y=270, width=100, height=100))
|
||||
|
||||
window.drag(Handle.TOP_WINDOW_BAR, direction=RIGHT(50))
|
||||
self.assertEqual(window.rect, Rect(x=350, y=270, width=100, height=100))
|
||||
|
||||
window.drag(Handle.TOP_WINDOW_BAR, direction=DOWN(30) + LEFT(70))
|
||||
self.assertEqual(window.rect, Rect(x=280, y=300, width=100, height=100))
|
||||
|
||||
def test_screen_limits_window_moves(self):
|
||||
screen = Screen(self._driver, ScreenPosition.RELATIVE,
|
||||
x=200, y=200, width=300, height=300)
|
||||
window = Window(screen, x=300, y=300, width=100, height=100)
|
||||
self.assertEqual(window.rect, Rect(x=300, y=300, width=100, height=100))
|
||||
|
||||
window.drag(Handle.TOP_WINDOW_BAR, direction=LEFT(300))
|
||||
self.assertEqual(window.frame_rect.x, screen.rect.x - window.frame_rect.width / 2)
|
||||
|
||||
def test_screen_in_scroll_container_limits_window_moves(self):
|
||||
screen = Screen(self._driver, ScreenPosition.IN_SCROLL_CONTAINER,
|
||||
x=200, y=2000, width=300, height=300,
|
||||
container_width=500, container_height=7000)
|
||||
screen.scroll_to()
|
||||
window = Window(screen, x=300, y=2100, width=100, height=100)
|
||||
self.assertEqual(window.rect, Rect(x=300, y=2100, width=100, height=100))
|
||||
|
||||
window.drag(Handle.TOP_WINDOW_BAR, direction=LEFT(300))
|
||||
self.assertEqual(window.frame_rect.x, screen.rect.x - window.frame_rect.width / 2)
|
||||
|
||||
def test_maximize(self):
|
||||
screen = Screen(self._driver, ScreenPosition.RELATIVE,
|
||||
x=200, y=200, width=300, height=300)
|
||||
window = Window(screen, x=300, y=300, width=100, height=100, title='Maximize')
|
||||
self.assertEqual(window.rect, Rect(x=300, y=300, width=100, height=100))
|
||||
|
||||
window.maximize()
|
||||
self.assertEqual(window.frame_rect, Rect(x=200, y=200, width=300, height=300))
|
||||
|
||||
def test_multitouch_window_move(self):
|
||||
screen = Screen(self._driver, ScreenPosition.FIXED,
|
||||
x=0, y=0, width=800, height=800)
|
||||
windows = [Window(screen, x=50, y=50, width=100, height=100, title='First'),
|
||||
Window(screen, x=400, y=400, width=100, height=100, title='Second'),
|
||||
Window(screen, x=50, y=400, width=100, height=100, title='Third')]
|
||||
self.assertEqual(windows[0].rect, Rect(x=50, y=50, width=100, height=100))
|
||||
self.assertEqual(windows[1].rect, Rect(x=400, y=400, width=100, height=100))
|
||||
self.assertEqual(windows[2].rect, Rect(x=50, y=400, width=100, height=100))
|
||||
|
||||
actions = [TouchDragAction(origin=windows[0].at(Handle.TOP_WINDOW_BAR), direction=DOWN(20) + RIGHT(20)),
|
||||
TouchDragAction(origin=windows[1].at(Handle.TOP_WINDOW_BAR), direction=DOWN(20) + LEFT(20)),
|
||||
TouchDragAction(origin=windows[2].at(Handle.TOP_WINDOW_BAR), direction=UP(20) + RIGHT(20))]
|
||||
perform_touch_drag_actions(actions)
|
||||
self.assertEqual(windows[0].rect, Rect(x=70, y=70, width=100, height=100))
|
||||
self.assertEqual(windows[1].rect, Rect(x=380, y=420, width=100, height=100))
|
||||
self.assertEqual(windows[2].rect, Rect(x=70, y=380, width=100, height=100))
|
||||
|
||||
def test_multitouch_window_resize(self):
|
||||
screen = Screen(self._driver, ScreenPosition.FIXED,
|
||||
x=0, y=0, width=800, height=800)
|
||||
windows = [Window(screen, x=50, y=50, width=150, height=150, title='First'),
|
||||
Window(screen, x=400, y=400, width=150, height=150, title='Second'),
|
||||
Window(screen, x=50, y=400, width=150, height=150, title='Third')]
|
||||
self.assertEqual(windows[0].rect, Rect(x=50, y=50, width=150, height=150))
|
||||
self.assertEqual(windows[1].rect, Rect(x=400, y=400, width=150, height=150))
|
||||
self.assertEqual(windows[2].rect, Rect(x=50, y=400, width=150, height=150))
|
||||
|
||||
actions = [TouchDragAction(origin=windows[0].at(Handle.TOP_LEFT), direction=DOWN(20) + RIGHT(20)),
|
||||
TouchDragAction(origin=windows[1].at(Handle.TOP), direction=DOWN(20) + LEFT(20)),
|
||||
TouchDragAction(origin=windows[2].at(Handle.BOTTOM_RIGHT), direction=UP(20) + RIGHT(20))]
|
||||
perform_touch_drag_actions(actions)
|
||||
self.assertEqual(windows[0].rect, Rect(x=70, y=70, width=130, height=130))
|
||||
self.assertEqual(windows[1].rect, Rect(x=400, y=420, width=150, height=130))
|
||||
self.assertEqual(windows[2].rect, Rect(x=50, y=400, width=170, height=130))
|
||||
|
||||
def tearDown(self):
|
||||
self._driver.quit()
|
||||
|
||||
|
||||
class ScreenPosition(Enum):
|
||||
FIXED = auto()
|
||||
RELATIVE = auto()
|
||||
IN_SCROLL_CONTAINER = auto()
|
||||
|
||||
|
||||
class Screen:
|
||||
def __init__(self, driver, positioning, x, y, width, height, container_width=0, container_height=0):
|
||||
self.driver = driver
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.width = width
|
||||
self.height = height
|
||||
if positioning == ScreenPosition.FIXED:
|
||||
command = f'initializeScreenWithFixedPosition({self.x}, {self.y}, {self.width}, {self.height})'
|
||||
elif positioning == ScreenPosition.RELATIVE:
|
||||
command = f'initializeScreenWithRelativePosition({self.x}, {self.y}, {self.width}, {self.height})'
|
||||
elif positioning == ScreenPosition.IN_SCROLL_CONTAINER:
|
||||
command = f'initializeScreenInScrollContainer({container_width}, {container_height}, {self.x}, {self.y}, {self.width}, {self.height})'
|
||||
self.element = self.driver.execute_script(
|
||||
f'''
|
||||
return testSupport.{command};
|
||||
'''
|
||||
)
|
||||
if positioning == ScreenPosition.IN_SCROLL_CONTAINER:
|
||||
self.element = self.element[1]
|
||||
|
||||
screen_information = call_instance_function(
|
||||
self.driver, 'screenInformation')
|
||||
if len(screen_information) != 1:
|
||||
raise AssertionError('Expecting exactly one screen_information!')
|
||||
self.screen_info = screen_information[0]
|
||||
|
||||
@property
|
||||
def rect(self):
|
||||
self.screen_info = call_instance_function(
|
||||
self.driver, 'screenInformation')[0]
|
||||
geo = self.screen_info['geometry']
|
||||
return Rect(geo['x'], geo['y'], geo['width'], geo['height'])
|
||||
|
||||
def scroll_to(self):
|
||||
ActionChains(self.driver).scroll_to_element(self.element).perform()
|
||||
|
||||
|
||||
class Window:
|
||||
def __init__(self, screen, x, y, width, height, title='title'):
|
||||
self.driver = screen.driver
|
||||
self.title = title
|
||||
self.driver.execute_script(
|
||||
f'''
|
||||
instance.createWindow({x}, {y}, {width}, {height}, '{screen.screen_info["name"]}', '{title}');
|
||||
'''
|
||||
)
|
||||
self._window_id = self.__window_information()['id']
|
||||
self.element = screen.element.shadow_root.find_element(
|
||||
By.CSS_SELECTOR, f'#qt-window-{self._window_id}')
|
||||
|
||||
def __window_information(self):
|
||||
information = call_instance_function(self.driver, 'windowInformation')
|
||||
return next(filter(lambda e: e['title'] == self.title, information))
|
||||
|
||||
@property
|
||||
def rect(self):
|
||||
geo = self.__window_information()["geometry"]
|
||||
return Rect(geo['x'], geo['y'], geo['width'], geo['height'])
|
||||
|
||||
@property
|
||||
def frame_rect(self):
|
||||
geo = self.__window_information()["frameGeometry"]
|
||||
return Rect(geo['x'], geo['y'], geo['width'], geo['height'])
|
||||
|
||||
def drag(self, handle, direction):
|
||||
ActionChains(self.driver) \
|
||||
.move_to_element_with_offset(self.element, *self.at(handle)['offset']) \
|
||||
.click_and_hold() \
|
||||
.move_by_offset(*translate_direction_to_offset(direction)) \
|
||||
.release().perform()
|
||||
|
||||
def maximize(self):
|
||||
maximize_button = self.element.find_element(
|
||||
By.CSS_SELECTOR, f'.title-bar :nth-child(6)')
|
||||
maximize_button.click()
|
||||
|
||||
def at(self, handle):
|
||||
""" Returns (window, offset) for given handle on window"""
|
||||
width = self.frame_rect.width
|
||||
height = self.frame_rect.height
|
||||
|
||||
if handle == Handle.TOP_LEFT:
|
||||
offset = (-width/2, -height/2)
|
||||
elif handle == Handle.TOP:
|
||||
offset = (0, -height/2)
|
||||
elif handle == Handle.TOP_RIGHT:
|
||||
offset = (width/2, -height/2)
|
||||
elif handle == Handle.LEFT:
|
||||
offset = (-width/2, 0)
|
||||
elif handle == Handle.RIGHT:
|
||||
offset = (width/2, 0)
|
||||
elif handle == Handle.BOTTOM_LEFT:
|
||||
offset = (-width/2, height/2)
|
||||
elif handle == Handle.BOTTOM:
|
||||
offset = (0, height/2)
|
||||
elif handle == Handle.BOTTOM_RIGHT:
|
||||
offset = (width/2, height/2)
|
||||
elif handle == Handle.TOP_WINDOW_BAR:
|
||||
frame_top = self.frame_rect.y
|
||||
client_area_top = self.rect.y
|
||||
top_frame_bar_width = client_area_top - frame_top
|
||||
offset = (0, -height/2 + top_frame_bar_width/2)
|
||||
return {'window': self, 'offset': offset}
|
||||
|
||||
|
||||
class TouchDragAction:
|
||||
def __init__(self, origin, direction):
|
||||
self.origin = origin
|
||||
self.direction = direction
|
||||
self.step = 2
|
||||
|
||||
|
||||
def perform_touch_drag_actions(actions):
|
||||
driver = actions[0].origin['window'].driver
|
||||
touch_action_builder = ActionBuilder(driver)
|
||||
pointers = [PointerActions(source=touch_action_builder.add_pointer_input(
|
||||
POINTER_TOUCH, f'touch_input_{i}')) for i in range(len(actions))]
|
||||
|
||||
for action, pointer in zip(actions, pointers):
|
||||
pointer.move_to(
|
||||
action.origin['window'].element, *action.origin['offset'])
|
||||
pointer.pointer_down(width=10, height=10, pressure=1)
|
||||
moves = [translate_direction_to_offset(a.direction) for a in actions]
|
||||
|
||||
def movement_finished():
|
||||
for move in moves:
|
||||
if move != (0, 0):
|
||||
return False
|
||||
return True
|
||||
|
||||
def sign(num):
|
||||
if num > 0:
|
||||
return 1
|
||||
elif num < 0:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
while not movement_finished():
|
||||
for i in range(len(actions)):
|
||||
pointer = pointers[i]
|
||||
move = moves[i]
|
||||
step = actions[i].step
|
||||
|
||||
current_move = (
|
||||
min(abs(move[0]), step) * sign(move[0]), min(abs(move[1]), step) * sign(move[1]))
|
||||
moves[i] = (move[0] - current_move[0], move[1] - current_move[1])
|
||||
pointer.move_by(current_move[0],
|
||||
current_move[1], width=10, height=10)
|
||||
for pointer in pointers:
|
||||
pointer.pointer_up()
|
||||
|
||||
touch_action_builder.perform()
|
||||
|
||||
|
||||
class TouchDragAction:
|
||||
def __init__(self, origin, direction):
|
||||
self.origin = origin
|
||||
self.direction = direction
|
||||
self.step = 2
|
||||
|
||||
|
||||
def perform_touch_drag_actions(actions):
|
||||
driver = actions[0].origin['window'].driver
|
||||
touch_action_builder = ActionBuilder(driver)
|
||||
pointers = [PointerActions(source=touch_action_builder.add_pointer_input(
|
||||
POINTER_TOUCH, f'touch_input_{i}')) for i in range(len(actions))]
|
||||
|
||||
for action, pointer in zip(actions, pointers):
|
||||
pointer.move_to(
|
||||
action.origin['window'].element, *action.origin['offset'])
|
||||
pointer.pointer_down(width=10, height=10, pressure=1)
|
||||
|
||||
moves = [translate_direction_to_offset(a.direction) for a in actions]
|
||||
|
||||
def movement_finished():
|
||||
for move in moves:
|
||||
if move != (0, 0):
|
||||
return False
|
||||
return True
|
||||
|
||||
def sign(num):
|
||||
if num > 0:
|
||||
return 1
|
||||
elif num < 0:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
while not movement_finished():
|
||||
for i in range(len(actions)):
|
||||
pointer = pointers[i]
|
||||
move = moves[i]
|
||||
step = actions[i].step
|
||||
|
||||
current_move = (
|
||||
min(abs(move[0]), step) * sign(move[0]), min(abs(move[1]), step) * sign(move[1]))
|
||||
moves[i] = (move[0] - current_move[0], move[1] - current_move[1])
|
||||
pointer.move_by(current_move[0],
|
||||
current_move[1], width=10, height=10)
|
||||
|
||||
for pointer in pointers:
|
||||
pointer.pointer_up()
|
||||
|
||||
touch_action_builder.perform()
|
||||
|
||||
|
||||
def translate_direction_to_offset(direction):
|
||||
return (direction.val[1] - direction.val[3], direction.val[2] - direction.val[0])
|
||||
|
||||
|
||||
def call_instance_function(driver, name):
|
||||
return driver.execute_script(
|
||||
f'''let result;
|
||||
window.{name}Callback = data => result = data;
|
||||
instance.{name}();
|
||||
return eval(result);''')
|
||||
|
||||
|
||||
class Direction:
|
||||
def __init__(self):
|
||||
self.val = (0, 0, 0, 0)
|
||||
|
||||
def __init__(self, north, east, south, west):
|
||||
self.val = (north, east, south, west)
|
||||
|
||||
def __add__(self, other):
|
||||
return Direction(self.val[0] + other.val[0],
|
||||
self.val[1] + other.val[1],
|
||||
self.val[2] + other.val[2],
|
||||
self.val[3] + other.val[3])
|
||||
|
||||
|
||||
class UP(Direction):
|
||||
def __init__(self, step=1):
|
||||
self.val = (step, 0, 0, 0)
|
||||
|
||||
|
||||
class RIGHT(Direction):
|
||||
def __init__(self, step=1):
|
||||
self.val = (0, step, 0, 0)
|
||||
|
||||
|
||||
class DOWN(Direction):
|
||||
def __init__(self, step=1):
|
||||
self.val = (0, 0, step, 0)
|
||||
|
||||
|
||||
class LEFT(Direction):
|
||||
def __init__(self, step=1):
|
||||
self.val = (0, 0, 0, step)
|
||||
|
||||
|
||||
class Handle(Enum):
|
||||
TOP_LEFT = auto()
|
||||
TOP = auto()
|
||||
TOP_RIGHT = auto()
|
||||
LEFT = auto()
|
||||
RIGHT = auto()
|
||||
BOTTOM_LEFT = auto()
|
||||
BOTTOM = auto()
|
||||
BOTTOM_RIGHT = auto()
|
||||
TOP_WINDOW_BAR = auto()
|
||||
|
||||
|
||||
class Rect:
|
||||
def __init__(self, x, y, width, height) -> None:
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
def __str__(self):
|
||||
return f'(x: {self.x}, y: {self.y}, width: {self.width}, height: {self.height})'
|
||||
|
||||
|
||||
def assert_rects_equal(geo1, geo2, msg=None):
|
||||
if geo1.x != geo2.x or geo1.y != geo2.y or geo1.width != geo2.width or geo1.height != geo2.height:
|
||||
raise AssertionError(f'Rectangles not equal: \n{geo1} \nvs \n{geo2}')
|
||||
|
||||
|
||||
unittest.main()
|
149
tests/manual/wasm/qwasmwindow/qwasmwindow_harness.cpp
Normal file
149
tests/manual/wasm/qwasmwindow/qwasmwindow_harness.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QtCore/QEvent>
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qregularexpression.h>
|
||||
#include <QtGui/qscreen.h>
|
||||
#include <QtGui/qwindow.h>
|
||||
#include <QtGui/qguiapplication.h>
|
||||
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/bind.h>
|
||||
#include <emscripten/val.h>
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
class DeleteOnCloseWindow : public QWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
void closeEvent(QCloseEvent *ev) override
|
||||
{
|
||||
Q_UNUSED(ev);
|
||||
delete this;
|
||||
}
|
||||
};
|
||||
|
||||
using namespace emscripten;
|
||||
|
||||
std::string toJSArray(const std::vector<std::string> &elements)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "[";
|
||||
bool comma = false;
|
||||
for (const auto &element : elements) {
|
||||
out << (comma ? "," : "");
|
||||
out << element;
|
||||
comma = true;
|
||||
}
|
||||
out << "]";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string toJSString(const QString &qstring)
|
||||
{
|
||||
Q_ASSERT_X(([qstring]() {
|
||||
static QRegularExpression unescapedQuoteRegex(R"re((?:^|[^\\])')re");
|
||||
return qstring.indexOf(unescapedQuoteRegex) == -1;
|
||||
})(),
|
||||
Q_FUNC_INFO, "Unescaped single quotes found");
|
||||
return "'" + qstring.toStdString() + "'";
|
||||
}
|
||||
|
||||
std::string rectToJSObject(const QRect &rect)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "{"
|
||||
<< " x: " << std::to_string(rect.x()) << ","
|
||||
<< " y: " << std::to_string(rect.y()) << ","
|
||||
<< " width: " << std::to_string(rect.width()) << ","
|
||||
<< " height: " << std::to_string(rect.height()) << "}";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string screenToJSObject(const QScreen &screen)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "{"
|
||||
<< " name: " << toJSString(screen.name()) << ","
|
||||
<< " geometry: " << rectToJSObject(screen.geometry()) << "}";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string windowToJSObject(const QWindow &window)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "{"
|
||||
<< " id: " << std::to_string(window.winId()) << ","
|
||||
<< " geometry: " << rectToJSObject(window.geometry()) << ","
|
||||
<< " frameGeometry: " << rectToJSObject(window.frameGeometry()) << ","
|
||||
<< " title: '" << window.title().toStdString() << "' }";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
void windowInformation()
|
||||
{
|
||||
auto windows = qGuiApp->allWindows();
|
||||
|
||||
std::vector<std::string> windowsAsJsObjects;
|
||||
windowsAsJsObjects.reserve(windows.size());
|
||||
std::transform(windows.begin(), windows.end(), std::back_inserter(windowsAsJsObjects),
|
||||
[](const QWindow *window) { return windowToJSObject(*window); });
|
||||
|
||||
emscripten::val::global("window").call<void>("windowInformationCallback",
|
||||
emscripten::val(toJSArray(windowsAsJsObjects)));
|
||||
}
|
||||
|
||||
void screenInformation()
|
||||
{
|
||||
auto screens = qGuiApp->screens();
|
||||
|
||||
std::vector<std::string> screensAsJsObjects;
|
||||
screensAsJsObjects.reserve(screens.size());
|
||||
std::transform(screens.begin(), screens.end(), std::back_inserter(screensAsJsObjects),
|
||||
[](const QScreen *screen) { return screenToJSObject(*screen); });
|
||||
emscripten::val::global("window").call<void>("screenInformationCallback",
|
||||
emscripten::val(toJSArray(screensAsJsObjects)));
|
||||
}
|
||||
|
||||
void createWindow(int x, int y, int w, int h, std::string screenId, std::string title)
|
||||
{
|
||||
auto screens = qGuiApp->screens();
|
||||
auto screen_it = std::find_if(screens.begin(), screens.end(), [&screenId](QScreen *screen) {
|
||||
return screen->name() == QString::fromLatin1(screenId);
|
||||
});
|
||||
if (screen_it == screens.end()) {
|
||||
qWarning() << "No such screen: " << screenId;
|
||||
return;
|
||||
}
|
||||
|
||||
auto *window = new DeleteOnCloseWindow;
|
||||
|
||||
window->setFlag(Qt::WindowTitleHint);
|
||||
window->setFlag(Qt::WindowMaximizeButtonHint);
|
||||
window->setTitle(QString::fromLatin1(title));
|
||||
window->setGeometry(x, y, w, h);
|
||||
window->setScreen(*screen_it);
|
||||
window->showNormal();
|
||||
}
|
||||
|
||||
EMSCRIPTEN_BINDINGS(qwasmwindow)
|
||||
{
|
||||
emscripten::function("screenInformation", &screenInformation);
|
||||
emscripten::function("windowInformation", &windowInformation);
|
||||
emscripten::function("createWindow", &createWindow);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
app.exec();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "qwasmwindow_harness.moc"
|
66
tests/manual/wasm/qwasmwindow/qwasmwindow_harness.html
Normal file
66
tests/manual/wasm/qwasmwindow/qwasmwindow_harness.html
Normal file
@ -0,0 +1,66 @@
|
||||
<!doctype html>
|
||||
|
||||
<head>
|
||||
<script type="text/javascript" src="qwasmwindow_harness.js"></script>
|
||||
<script>
|
||||
(async () => {
|
||||
const instance = await createQtAppInstance({});
|
||||
window.instance = instance;
|
||||
|
||||
const testSandbox = document.createElement('div');
|
||||
testSandbox.id = 'test-sandbox';
|
||||
document.body.appendChild(testSandbox);
|
||||
|
||||
const makeSizedDiv = (left, top, width, height) => {
|
||||
const screenDiv = document.createElement('div');
|
||||
|
||||
screenDiv.style.left = `${left}px`;
|
||||
screenDiv.style.top = `${top}px`;
|
||||
screenDiv.style.width = `${width}px`;
|
||||
screenDiv.style.height = `${height}px`;
|
||||
screenDiv.style.backgroundColor = 'lightblue';
|
||||
|
||||
return screenDiv;
|
||||
};
|
||||
|
||||
window.testSupport = {
|
||||
initializeScreenWithFixedPosition: (left, top, width, height) => {
|
||||
const screenDiv = makeSizedDiv(left, top, width, height);
|
||||
testSandbox.appendChild(screenDiv);
|
||||
|
||||
screenDiv.style.position = 'fixed';
|
||||
instance.qtAddContainerElement(screenDiv);
|
||||
|
||||
return screenDiv;
|
||||
},
|
||||
initializeScreenWithRelativePosition: (left, top, width, height) => {
|
||||
const screenDiv = makeSizedDiv(left, top, width, height);
|
||||
testSandbox.appendChild(screenDiv);
|
||||
|
||||
screenDiv.style.position = 'relative';
|
||||
instance.qtAddContainerElement(screenDiv);
|
||||
|
||||
return screenDiv;
|
||||
},
|
||||
initializeScreenInScrollContainer:
|
||||
(scrollWidth, scrollHeight, left, top, width, height) => {
|
||||
const scrollContainer = document.createElement('div');
|
||||
scrollContainer.style.height = `${scrollHeight}px`;
|
||||
scrollContainer.style.width = `${scrollWidth}px`;
|
||||
testSandbox.appendChild(scrollContainer);
|
||||
|
||||
const screenDiv = makeSizedDiv(left, top, width, height);
|
||||
scrollContainer.appendChild(screenDiv);
|
||||
screenDiv.style.position = 'relative';
|
||||
|
||||
instance.qtAddContainerElement(screenDiv);
|
||||
|
||||
return [scrollContainer, screenDiv];
|
||||
}
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
</body>
|
23
tests/manual/wasm/qwasmwindow/run.sh
Normal file
23
tests/manual/wasm/qwasmwindow/run.sh
Normal file
@ -0,0 +1,23 @@
|
||||
#! /bin/bash
|
||||
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
set -m
|
||||
|
||||
function removeServer()
|
||||
{
|
||||
[ -z "$cleanupPid" ] || kill $cleanupPid
|
||||
}
|
||||
|
||||
trap removeServer EXIT
|
||||
|
||||
script_dir=`dirname ${BASH_SOURCE[0]}`
|
||||
cd "$script_dir"
|
||||
python3 qtwasmserver.py -p 8001 > /dev/null 2>&1 &
|
||||
cleanupPid=$!
|
||||
|
||||
python3 qwasmwindow.py $@
|
||||
|
||||
echo 'Press any key to continue...' >&2
|
||||
read -n 1
|
Reference in New Issue
Block a user