mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 06:11:16 +00:00
Add a qt_viewer example that uses QWidget
This commit is contained in:
parent
90d3953d42
commit
1dba04721a
12 changed files with 249 additions and 1 deletions
|
@ -18,4 +18,5 @@ add_subdirectory(examples/printerdemo/cpp_interpreted/)
|
||||||
add_subdirectory(examples/todo/cpp/)
|
add_subdirectory(examples/todo/cpp/)
|
||||||
add_subdirectory(examples/gallery/)
|
add_subdirectory(examples/gallery/)
|
||||||
add_subdirectory(examples/memory/)
|
add_subdirectory(examples/memory/)
|
||||||
|
add_subdirectory(examples/qt_viewer/)
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,12 @@ LICENSE END */
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
#define SIXTYFPS_QT_INTEGRATION // In the future, should be defined by cmake only if this is enabled
|
||||||
|
#ifdef SIXTYFPS_QT_INTEGRATION
|
||||||
|
class QWidget;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
namespace sixtyfps::cbindgen_private {
|
namespace sixtyfps::cbindgen_private {
|
||||||
// This has to stay opaque, but VRc don't compile if it is just forward declared
|
// This has to stay opaque, but VRc don't compile if it is just forward declared
|
||||||
struct ErasedComponentBox : vtable::Dyn
|
struct ErasedComponentBox : vtable::Dyn
|
||||||
|
@ -501,6 +507,17 @@ public:
|
||||||
cbindgen_private::sixtyfps_run_event_loop();
|
cbindgen_private::sixtyfps_run_event_loop();
|
||||||
hide();
|
hide();
|
||||||
}
|
}
|
||||||
|
#ifdef SIXTYFPS_QT_INTEGRATION
|
||||||
|
/// Return a QWidget for this instance.
|
||||||
|
/// This function is only available if the qt graphical backend was compiled in, and
|
||||||
|
/// it may return nullptr if the Qt backend is not used at runtime.
|
||||||
|
QWidget *qwidget() const {
|
||||||
|
cbindgen_private::ComponentWindowOpaque win;
|
||||||
|
cbindgen_private::sixtyfps_interpreter_component_instance_window(inner(), &win);
|
||||||
|
return reinterpret_cast<QWidget *>(cbindgen_private::sixtyfps_qt_get_widget(
|
||||||
|
reinterpret_cast<cbindgen_private::ComponentWindow *>(&win)));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool set_property(std::string_view name, const Value &value) const
|
bool set_property(std::string_view name, const Value &value) const
|
||||||
{
|
{
|
||||||
|
|
24
examples/qt_viewer/CMakeLists.txt
Normal file
24
examples/qt_viewer/CMakeLists.txt
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# LICENSE BEGIN
|
||||||
|
# This file is part of the SixtyFPS Project -- https://sixtyfps.io
|
||||||
|
# Copyright (c) 2020 Olivier Goffart <olivier.goffart@sixtyfps.io>
|
||||||
|
# Copyright (c) 2020 Simon Hausmann <simon.hausmann@sixtyfps.io>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
# This file is also available under commercial licensing terms.
|
||||||
|
# Please contact info@sixtyfps.io for more information.
|
||||||
|
# LICENSE END
|
||||||
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
project(qt_viewer LANGUAGES CXX)
|
||||||
|
|
||||||
|
if (NOT TARGET SixtyFPS::SixtyFPS)
|
||||||
|
find_package(SixtyFPS REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(Qt5 COMPONENTS Core Widgets)
|
||||||
|
|
||||||
|
if (Qt5Widgets_FOUND)
|
||||||
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
add_executable(qt_viewer qt_viewer.cpp)
|
||||||
|
target_link_libraries(qt_viewer PRIVATE SixtyFPS::SixtyFPS Qt5::Core Qt5::Widgets)
|
||||||
|
endif(Qt5Widgets_FOUND)
|
||||||
|
|
7
examples/qt_viewer/README.md
Normal file
7
examples/qt_viewer/README.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# qt_viewer
|
||||||
|
|
||||||
|
This is an example that shows how to embed a dynamically loaded .60 into a Qt (QWidgets) application
|
||||||
|
|
||||||
|
The trick is that it uses the C++ `sixtyfps::interpreter::ComponentInstance::qwidget` and embed
|
||||||
|
that widget in a Qt application.
|
||||||
|
|
78
examples/qt_viewer/interface.ui
Normal file
78
examples/qt_viewer/interface.ui
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Interface</class>
|
||||||
|
<widget class="QWidget" name="Interface">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>512</width>
|
||||||
|
<height>524</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QPushButton" name="load_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>Load</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1" rowspan="7">
|
||||||
|
<widget class="QFrame" name="my_content">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Property name</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLineEdit" name="prop_name"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Value</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLineEdit" name="prop_value"/>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QPushButton" name="set_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>Set</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>233</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
73
examples/qt_viewer/qt_viewer.cpp
Normal file
73
examples/qt_viewer/qt_viewer.cpp
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/* LICENSE BEGIN
|
||||||
|
This file is part of the SixtyFPS Project -- https://sixtyfps.io
|
||||||
|
Copyright (c) 2020 Olivier Goffart <olivier.goffart@sixtyfps.io>
|
||||||
|
Copyright (c) 2020 Simon Hausmann <simon.hausmann@sixtyfps.io>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
This file is also available under commercial licensing terms.
|
||||||
|
Please contact info@sixtyfps.io for more information.
|
||||||
|
LICENSE END */
|
||||||
|
|
||||||
|
#include <QtWidgets/QtWidgets>
|
||||||
|
#include <sixtyfps_interpreter.h>
|
||||||
|
|
||||||
|
#include "ui_interface.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct LoadedFile {
|
||||||
|
sixtyfps::ComponentHandle<sixtyfps::interpreter::ComponentInstance> instance;
|
||||||
|
QWidget *widget;
|
||||||
|
};
|
||||||
|
|
||||||
|
void show_diagnostics(QWidget *root, const sixtyfps::SharedVector< sixtyfps::interpreter::Diagnostic > &diags) {
|
||||||
|
QString text;
|
||||||
|
|
||||||
|
for (auto diagnostic : diags) {
|
||||||
|
text += (diagnostic.level == sixtyfps::interpreter::DiagnosticLevel::Warning
|
||||||
|
? QApplication::translate("qt_viewer", "warning: %1\n")
|
||||||
|
: QApplication::translate("qt_viewer", "error: %1\n")
|
||||||
|
).arg(QString::fromUtf8(diagnostic.message.data()));
|
||||||
|
|
||||||
|
text += QApplication::translate("qt_viewer", "location: %1").arg(QString::fromUtf8(diagnostic.source_file.data()));
|
||||||
|
if (diagnostic.line > 0)
|
||||||
|
text += ":" + QString::number(diagnostic.line);
|
||||||
|
if (diagnostic.column > 0)
|
||||||
|
text += ":" + QString::number(diagnostic.column);
|
||||||
|
text += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
QMessageBox::critical(root, QApplication::translate("qt_viewer", "Compilation error"), text, QMessageBox::StandardButton::Ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
std::unique_ptr<LoadedFile> loaded_file;
|
||||||
|
|
||||||
|
QWidget main;
|
||||||
|
Ui::Interface ui;
|
||||||
|
ui.setupUi(&main);
|
||||||
|
QHBoxLayout layout(ui.my_content);
|
||||||
|
|
||||||
|
QObject::connect(ui.load_button, &QPushButton::clicked, [&] {
|
||||||
|
QString fileName = QFileDialog::getOpenFileName(
|
||||||
|
&main, QApplication::translate("qt_viewer", "Open SixtyFPS File"), {},
|
||||||
|
QApplication::translate("qt_viewer", "SixtyFPS File (*.60)"));
|
||||||
|
if (fileName.isEmpty())
|
||||||
|
return;
|
||||||
|
loaded_file.reset();
|
||||||
|
sixtyfps::interpreter::ComponentCompiler compiler;
|
||||||
|
auto def = compiler.build_from_path(fileName.toUtf8().data());
|
||||||
|
if (!def) {
|
||||||
|
show_diagnostics(&main, compiler.diagnostics());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto instance = def->create();
|
||||||
|
QWidget *wid = instance->qwidget();
|
||||||
|
wid->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
layout.addWidget(wid);
|
||||||
|
loaded_file = std::make_unique<LoadedFile>(LoadedFile{ instance, wid });
|
||||||
|
});
|
||||||
|
main.show();
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
|
|
|
@ -65,6 +65,9 @@ pub trait PlatformWindow {
|
||||||
&self,
|
&self,
|
||||||
source: Pin<&crate::properties::Property<ImageReference>>,
|
source: Pin<&crate::properties::Property<ImageReference>>,
|
||||||
) -> crate::graphics::Size;
|
) -> crate::graphics::Size;
|
||||||
|
|
||||||
|
/// Return self as any so the backend can upcast
|
||||||
|
fn as_any(&self) -> &dyn core::any::Any;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Structure that represent a Window in the runtime
|
/// Structure that represent a Window in the runtime
|
||||||
|
|
|
@ -420,6 +420,23 @@ pub extern "C" fn sixtyfps_interpreter_component_instance_show(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a window for the component
|
||||||
|
///
|
||||||
|
/// The out pointer must be uninitialized and must be destroyed with
|
||||||
|
/// sixtyfps_component_window_drop after usage
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sixtyfps_interpreter_component_instance_window(
|
||||||
|
inst: &ErasedComponentBox,
|
||||||
|
out: *mut sixtyfps_corelib::window::ffi::ComponentWindowOpaque,
|
||||||
|
) {
|
||||||
|
use sixtyfps_corelib::window::ComponentWindow;
|
||||||
|
assert_eq!(
|
||||||
|
core::mem::size_of::<ComponentWindow>(),
|
||||||
|
core::mem::size_of::<sixtyfps_corelib::window::ffi::ComponentWindowOpaque>()
|
||||||
|
);
|
||||||
|
core::ptr::write(out as *mut ComponentWindow, inst.window())
|
||||||
|
}
|
||||||
|
|
||||||
/// Instantiate an instance from a definition.
|
/// Instantiate an instance from a definition.
|
||||||
///
|
///
|
||||||
/// The `out` must be uninitialized and is going to be initialized after the call
|
/// The `out` must be uninitialized and is going to be initialized after the call
|
||||||
|
|
|
@ -502,6 +502,10 @@ impl PlatformWindow for GraphicsWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MappedWindow {
|
struct MappedWindow {
|
||||||
|
|
|
@ -39,7 +39,8 @@ pub fn use_modules() -> usize {
|
||||||
}
|
}
|
||||||
#[cfg(not(no_qt))]
|
#[cfg(not(no_qt))]
|
||||||
{
|
{
|
||||||
(&widgets::NativeButtonVTable) as *const _ as usize
|
qt_window::ffi::sixtyfps_qt_get_widget as usize
|
||||||
|
+ (&widgets::NativeButtonVTable) as *const _ as usize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1010,6 +1010,10 @@ impl PlatformWindow for QtWindow {
|
||||||
})
|
})
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_font(request: FontRequest) -> QFont {
|
fn get_font(request: FontRequest) -> QFont {
|
||||||
|
@ -1199,3 +1203,19 @@ fn qt_key_to_string(key: key_generated::Qt_Key, event_text: String) -> SharedStr
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) mod ffi {
|
||||||
|
use std::any::Any;
|
||||||
|
use std::ffi::c_void;
|
||||||
|
|
||||||
|
use super::QtWindow;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn sixtyfps_qt_get_widget(
|
||||||
|
window: &sixtyfps_corelib::window::ComponentWindow,
|
||||||
|
) -> *mut c_void {
|
||||||
|
Any::downcast_ref(window.0.as_any()).map_or(std::ptr::null_mut(), |win: &QtWindow| {
|
||||||
|
win.widget_ptr().cast::<c_void>().as_ptr()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -130,6 +130,9 @@ cpp! {{
|
||||||
void ensure_initialized()
|
void ensure_initialized()
|
||||||
{
|
{
|
||||||
static auto app [[maybe_unused]] = []{
|
static auto app [[maybe_unused]] = []{
|
||||||
|
if (qApp) {
|
||||||
|
return qApp;
|
||||||
|
}
|
||||||
QCoreApplication::setAttribute(Qt::AA_PluginApplication, true);
|
QCoreApplication::setAttribute(Qt::AA_PluginApplication, true);
|
||||||
static int argc = 1;
|
static int argc = 1;
|
||||||
static char argv[] = "sixtyfps";
|
static char argv[] = "sixtyfps";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue