Code Monkey home page Code Monkey logo

cxx-qt's People

Contributors

ahayzen-kdab avatar alois31 avatar be-ing avatar dependabot[bot] avatar gerharddc avatar haata avatar jacquetc avatar jimmyvanhest avatar knoxfighter avatar leonmattheskdab avatar mattkdab avatar mmmike avatar montel avatar olivierldff avatar przempore avatar vimpostor avatar winterz avatar yuja avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cxx-qt's Issues

Consider which attributes should be on a Q_PROPERTY

Have the ability to declare a #[property(getter = "...", setter = "...", notify = "...")]

For example we can't currently say that a property is readonly to C++/QML, but note that this would require the setter to still be accessible from Rust. Does this simply mean which Q_PROPERTY attributes as set?

Add member functions to basic Qt types in Rust

  • Add missing member functions to types #416
  • Consider if we should adapt the Rust API to be "safe" / less Qt-like (eg QRectF set_x)

Original Description
Types like QPoint have a lot of member functions, as well as operators (i.e. +,-,/,*) that make them a lot more useful. We should give Rust access to these as well.

Use visibility for invokables rather than attribute

Instead of using #[invokable] to mark which methods should be exposed to Qt, use the visibility.

// This method is exposed to Qt as a Q_INVOKABLE
pub fn qt_invokable() {}

// This method is not exposed to Qt but other Rust objects can use it
pub(crate) fn rust_only_public_method() {}

// This method is private to only this Rust struct
fn rust_only_private_method() {}

Later we might also want to have #[cxx_qt(skip)] to skip pub invokables which could be related to #22 and #25

Remove Reliance on the sizeof(int), etc.

For the conversion of some types, we currently rely on the fact that sizeof(int) is 32 bit wide on most platforms.
Unfortunately, according to the C and C++ standards, this doesn't have to be the case. There might already exist platforms where an "int" is 64 bits wide.

Therefore instead of checking that an int is 32-bit wide, maybe the appropriate action is to change the data type we're using for the Rust side depending on the size of int.

We should probably also look into how CXX deals with this ๐Ÿค”

Consider if we can allow sub object to be returned

Currently nested objects can be used as properties and parameters. But they cannot be used as return types as we need return a pointer on the C++ side, but also ensure that the ownership is in a valid place (?). Eg we can't construct a sub object inside an invokable and then return it, as no one owns it.

Improve the acceptance test examples

Make the examples more logical so they have a property, parameter, return type for each type. And then have (de)serialise for each type. And then have a sub object test.

Use full module path in generated rust code to avoid collisions

Use full module path in generated source paths, so src/moda/lib.rs with the contents mod modb { mod modc { our macro around mod my_object } } would become src/moda/modb/modc/my_object.h. This helps later if we allow for generating objects into different QML namespaces.

At the moment it's possible to cause two modules to collide in generation.

There will be fun here around sub objects, might even simplfy code as sub objects use fully qualified names?

Consider how (de)serialise can work with custom types

For types such as QPoint consider how the user can use these with serde on the Data struct.

Options appear to be

  • Implement derive of serde always
  • Behind a feature implement serde derive helpers
  • Don't implement and force the user to impl the (de)seralise trait for the struct

Support u64 and i64

Add u64 and i64 support, qint64 and std::int64_t disagree on long long int vs long int so cxx becomes confused. And using just std means QML is confused (we need to register the types?).

Use qualified names for C++ includes

Currently our includes are just #include <QColor>, consider using #include <QtGui/QColor> so that it is obvious which Qt modules we need to link to in the cmake.

As this caused compile failures when developing due to missing non-obvious modules.

Have a handle_init / handle_destroy function and trait

Currently there is no way to perform any tasks at creation of the object that require access to the CppObj. (eg starting a background thread that takes the update_requester).

There also isn't a way to tidy up when the object is deconstructed, eg closing a thread / disconnect from db / network.

Add a HandleInit / HandleDestroy trait and method similar to handle_property_change, which is triggered by in the constructor / deconstructor of the object.

  • HandleInit / HandleConstructor trait
  • HandleDestroy / HandleDeconstructor trait

Can we (de)serialise subobjects somehow

Figure out how sub objects in Data can be (de)serialised ? At the moment subobjects are removed silently from the internal Data, so then the from impl of Data doesn't consider it.

We might need to manually implement the (de)serialise traits to have custom code.

Trying to build a object with no Data properties fails

We fail to generate a C++ from Rust File with the error explicit #[repr(...)] is required for enum without any variants, is this due to the Property enum being empty in the CXX bridge?

Solutions are one of the following

  • Add something like #[repr(i32)] to the enum (lose CXX automatic type choosing)
  • Remove enum Property when there aren't any
  • Inject fake value such as "LastProperty" or "NoProperties"

Remove Copy from cxx-qt-lib types and implement Clone properly

  • Remove Copy from structs eg QPointF
  • Don't implement impl From<&QPointF> for QPointF for trivial types
  • Implement Clone for trivial types that uses new constructor on C++ side, eg QPointF::new(x, y)
  • Remove impl From<&QVariant> for Variant for opaque types and just use the to_rust() ?
  • Change the generation in the impl<'a> From<&CppObj<'a>> for Data so that it uses .clone() or .to_rust() for the right values
    • Note that if #9 is implemented the CppObj getter will return a cloned/to_rust object already (?)

Transparently use the CppObjWrapper in invokables and other methods

Currently we have cpp: Pin<&mut CppObj> as the type to signify the Cpp instance. This works until you try to perform multiple mutations, and doesn't allow us to perform the Rust <-> C++ conversions transparently. This also means users can perform tasks that might be considered "unsafe" - eg triggering an invokable via the C++ side rather than Rust (could cause deadlock).

Currently users need to write let wrapper = CppObjWrapper::new(cpp); and then user this wrapper.

Instead use something like cpp: CppObj or cpp: CppObjWrapper as the type the user uses, then transparently generate another rust method which is called first and takes the Pin<&mut CppObj>, then creates the wrapper and calls the users method.

Create a Rust book

  • Write existing sections, eg QObject, Data, RustObj, invokable, properties, handle property change, threading update etc
  • Write future sections, eg QAbstractListModel, Roles, enum etc
  • When on Github integration with CI for Github pages? https://rust-lang.github.io/mdBook/continuous-integration.html#deploying-your-book-to-github-pages
  • Ensure we clearly describe which types are trivial and not in a table or something so that users know when to use Variant vs QVariant etc
  • Safety lines in qcolor etc have TODOs which could be linked to an advanced section in the book about internals

https://rust-lang.github.io/mdBook/

Figure out API for supporting list models

We need support for the following items

  • Row struct , which will declare roles (unless we use generic enums from #34 )
  • Have a Model or ItemModel trait which implements the model methods
  • Have support in the macro for defining different base object (eg cxx_qt(QAbstractItemModel))
  • Signal support for emitting signals #31
  • A way to trigger beginInsertRows / remove / reset ?
  • When a signal arrives and we handle it, we need a way to perform model tasks. Eg handle_signal needs to then trigger set_data or insert_row, but this needs to go back to the C++ side and not just be on the rust side like an invokable?
// what happens for qabstractitemmodel, qabstracttablemodel, qstandardmodel etc ?
#[make_qabstractlistmodel]
mod my_model {
    #[derive(Default)]
    pub struct Row {
        first_name: String,
        last_name: String,
    }
 
    #[derive(Default)]
    struct RustObj {
        items: Vec<Row>,
    }
 
    // TODO: need a way to trigger dataChanged() from another thread
 
    // TODO: need a way to trigger beginInsertRows / remove / reset / move etc
 
    impl unsafe Model<CppObj, Role> for RustObj {
        // TODO: what do we return here ? some kind of variant or an enum?
        fn data(&self, index: i32, role: Role) -> Option<Value> {
            if let Some(row) = self.items.get(index) {
                let value = match role {
                    Role::FirstName => row.first_name.into(),
                    Role::LastName => row.last_name.into(),
                }
                Some(value)
            } else {
                None
            }
        }
 
        // TODO: QAbstractItemModel takes a parent: QModelIndex
        fn row_count(&self) -> i32 {
            self.items.len()
        }
 
        fn set_data(&mut self, index: i32, value: Value, role: Role) -> bool {
            if let Some(row) = self.items.get_mut(index) {
                match role {
                    // TODO: how do we do the conversion here?
                    Role::FirstName => row.first_name = value.into(),
                    Role::LastName => row.last_name = value.into(),
                }
 
                // TODO: needs to emit dataChanged()
                // could we have an existing channel to send signals into?
                // or should developers handle this themselves
                self.model.send(Model::DataChanged{ first, last });
 
                true
            } else {
                false
            }
        }
    }

    impl UpdateRequestHandler<CppObj> for RustObj {
        fn handle_update_request(&mut self, cpp: &mut CppObj) {
           // From a custom channel we could perform signal updates here
            cpp.model_data_changed(first, last);
            cpp.model_rows_inserted(first, last);
            cpp.model_rows_moved(first, last);
            cpp.model_rows_removed(first, last);
            cpp.model_reset();
        }
    }
}

Try to enable deny(missing_docs) and fix any ignore code blocks in docs

Try to enable #![deny(missing_docs)] in all crates and ensure any ```ignore are removed from code blocks so we have real code blocks

  • book - this is harder and might need mdbook-keeper rust-lang/mdBook#706 so maybe not worth it as these are mostly included from real examples
  • cxx-qt - #456
  • cxx-qt-build - #456
  • cxx-qt-gen - #880
  • cxx-qt-lib
  • cxx-qt-lib-headers - #456
  • qt-build-utils - #880

https://doc.rust-lang.org/rustdoc/lints.html#missing_docs

Add support for types to QVariant

Primitives - #54

  • bool
  • f32
  • f64
  • i8
  • i16
  • i32
  • String
  • u8
  • u16
  • u32

Qt types - #65

  • QColor
  • QDate
  • QDateTime
  • QTime (?)
  • QPoint
  • QPointF
  • QRect
  • QRectF
  • QSize
  • QSizeF
  • QString (will this be the same / required as String?)
  • QUrl

Consider how to support isValid() === false vs an Unsupported type.

Change macros to use cxx_qt

Consider changing our macros to always have cxx_qt so we don't collide. This is similar to other crates such as serde. As currently we use macros like make_qobject and invokable these could collide with other crates.

Eg have cxx_qt(QObject), cxx_qt(QAbstractListModel), cxx_qt(invokable), cxx_qt(enum)

Automatically perform the to/from Rust type conversion inside the CppObjWrapper

Once we have #8 the user will always be using the CppObjWrapper. So then in the CppObjWrapper getter/setters etc change the types so that the parameters and return types are the "Rust" types and any conversion is performed inside the CppObjWrapper.

Eg instead of the user needing to change QVariant -> Variant. The CppObjWrapper can do the to_rust() conversion and return Variant.

Add support for super as the prefix for CppObj types

Currently we only allow crate::module::CppObj or CppObj as the type. Once we are able to track where a module is relative to the root, consider adding support for super::module::CppObj.

Currently this won't work as we need to build the namespace and other items from the module path, so we require a full module path.

We need the following to be true

  • Starts with crate/super (if more than one part) (TODO: check imports from other crates or modules that are already in use)
  • Ends with CppObj
  • Has one part or three+ parts

Rename C++ members and methods to avoid collisions

Rename C++ members so that more Q_PROPERTY names are available eg use m_internalRustObj, m_propertyObj, m_mutexInternalRustObj, m_mutexPropertyObj etc

So there could be the following prefixes

  • internal
  • property
  • mutex

Also consider if there are any names we cannot avoid collisions and we should error if they are used in Rust (and if this should move to another issue).

Document enabling LTO

Potentially document in the Rust Book how enabling lto in the cargo.toml of the project can reduce the plugin size (eg we went from 5MB โ†’ 2MB)

Method named a_b2 confuses something

If you have a method named a_b2 something gets confused and it looks for a_b_2. This is likely due to the convert_case going wrong somewhere.

Ensure all C++ code is pre clang-format

Not all of the gen_cpp code is formatted with clang-format's mozilla style, ensure that we are following that to reduce work by clang-format on our generated C++ code. (eg in the cpp files the return type is on the same line as the method definition not the previous).

Have a helper to register all types to the QML engine

Currently if you use the QQmlExtensionPlugin all generated types are registered to the QML engine.

But if you don't use the plugin then you currently have to register them manually.

Provide a method somewhere that does this automatically, eg CxxQtTypes::registerTypes(uri).

Move CxxQObject to be a member rather than inherited

We should change CxxQObject to be a member of the generated class rather than inherited.

This will then allow us to reuse the same object for other classes we generate (eg non-QObject ones).

This could look something like ThreadHelper m_threadHelper and then m_threadHelper->runOnGUIThread(...).

Support more types in the module

If you try to put a const VAR: T = V inside our macro module then it fails with unsupported type. We should pass these through like we do with use and enum etc.

Ability to declare and emit signals

  • Have the ability to write enums for signals
  • Have a handleSignal trait for listening to signals
  • CppObj has emit_queued(Signal) if there is a signal enum, and then uses runOnGUIThread
  • CppObj has unsafe emit_now(Signal) that is called now
  • If there is a handleSignal then in constructor of C++ obj we need to connect signals to a method which proxies to RustObj
  • Extract phase needs
    • signals enum
    • if there is a trait

Rust API would use enums, then have an emit method in the wrapper. We use named struct like enums so that the names can be used for the C++ signal arg.

enum Signal {
  Ready,
  DataChanged { first: i32, second: i32 }
}

impl RustObj {
  #[invokable]
  fn invokable(&self, cpp: &mut CppObj) {
    cpp.emit_queued(Signal::Ready { 1, 2 });
    cpp.emit_queued(Signal::DataChanged { 1, 2 });
  }
}

Internally the emit method destructures the enum and call a relevant C++ method.

impl CppObj {
  fn emit_queued(&mut self, signal: Signal) {
    match signal {
      Signal::Ready => self.cpp.as_mut().emit_ready(),
      Signal::DataChanged(first, second) => self.cpp.as_mut().emit_data_changed(first, second),
    }
  }
}

The public C++ side would then appear as

Q_SIGNALS:
  void ready();
  void dataChanged(qint32 first, qint32 second);

Then the C++ side would have methods for each of these signals to emit them on the GUI thread (these are called from Rust).

void MyObject::emitReady() {
  runOnGUIThread([&]() { Q_EMIT ready(); };
}

void MyObject::emitDataChanged(qint32 first, qint32 second) {
  runOnGUIThread([&]() { Q_EMIT dataChanged(); };
}

The flow is the following for a generic "MySignal" with "number".

# A signal being emitted from Rust
CppObj::emit_queued(Signal::MySignal { number }) -> MyObject::emit_my_signal(number) -> runOnGUIThread -> Q_EMIT MyObject::mySignal(number)

Implement borrowRustObj() on the CppObjWrapper

Have the ability to perform borrowRustObj on a sub objects CppObjWrapper (not our own)

  • Return Borrow and when Drop occurs it should trigger releaseRustObj
  • Use try_lock in borrowRustObj, panic / return Result<Borrow>, if user wants multithreading they should have their own locking
  • Borrow should take a ref of the CppWrapper and a closure for the impl Drop

This would allow the QML code below, where the tablemodel needs to clone() the database pool on the project RustObj to access the database connection.

Project { id: project; path: "/my/db" }
TableModel { project: project }

Have the ability to mark an object as singleton

Add option to mark an option as a singleton in the macro attribute, so make_object(singleton = true) or something.

Once #22 is done this might be cxx_qt(QObject, singleton = true) or cxx_qt(QObject(singleton = true)). Consider if we can hint the QML versions and name here as well.

Improve and investigate error messages

  • Investigate why errors from cxx-qt-gen don't point to the span - #455
  • Investigate if CXX errors can be exposed better (eg from cxx-qt-build) - #461
  • Investigate why macros don't error in IDEs from cxx-qt-gen errors (this was because i had "rust-analyzer.checkOnSave": false, set)
  • Can we get CXX errors to error in IDEs ? - #461
  • Check if we need to use the dummy env var to improve IDE experience

Original Description

  • Add error messages for invalid invokable input and return types
  • Add error messages for invalid property and invokable names
  • Consider how other messages we emitted when the macros code is wrong etc

Investigate behavior of object references in extern "C" code

Currently we sometimes declare extern "C" functions in C++ that take references as arguments.

As references aren't part of the C language, it's unclear what exactly this code compiles down to. Currently, this doesn't seem to cause issues under GCC, Clang or MSVC, but it is likely compiler dependent.

This is a tracking issue to reference, should this issue come up in the future.
We will not change our code for now, as CXX also uses references in this way, so CXX would need to be changed anyway.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.