mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-30 22:01:13 +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/gallery/)
|
||||
add_subdirectory(examples/memory/)
|
||||
add_subdirectory(examples/qt_viewer/)
|
||||
|
||||
|
|
|
@ -15,6 +15,12 @@ LICENSE END */
|
|||
|
||||
#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 {
|
||||
// This has to stay opaque, but VRc don't compile if it is just forward declared
|
||||
struct ErasedComponentBox : vtable::Dyn
|
||||
|
@ -501,6 +507,17 @@ public:
|
|||
cbindgen_private::sixtyfps_run_event_loop();
|
||||
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
|
||||
{
|
||||
|
|
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,
|
||||
source: Pin<&crate::properties::Property<ImageReference>>,
|
||||
) -> 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
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
/// 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 {
|
||||
|
|
|
@ -39,7 +39,8 @@ pub fn use_modules() -> usize {
|
|||
}
|
||||
#[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()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
static auto app [[maybe_unused]] = []{
|
||||
if (qApp) {
|
||||
return qApp;
|
||||
}
|
||||
QCoreApplication::setAttribute(Qt::AA_PluginApplication, true);
|
||||
static int argc = 1;
|
||||
static char argv[] = "sixtyfps";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue