Skip to content

Interface implementations can be provided by the component#10932

Open
0x6e wants to merge 3 commits intoslint-ui:masterfrom
0x6e:feature/permit-interface-implementations
Open

Interface implementations can be provided by the component#10932
0x6e wants to merge 3 commits intoslint-ui:masterfrom
0x6e:feature/permit-interface-implementations

Conversation

@0x6e
Copy link
Collaborator

@0x6e 0x6e commented Mar 3, 2026

Prior to this change interface implementations for properties and callbacks could only be provided by the compiler. This PR allows a component or the components it inherits from to provide the interface API. The compiler verifies that the provided implementation matches the interface, otherwise an error is emitted.

This change is necessary to allow us to implement the CheckBox interface in the Qt std-widgets style like this:

import { CheckBoxInterface } from "../common/std-widget-interfaces.slint";

export component CheckBox implements CheckBoxInterface inherits NativeCheckBox {
    /// ...
}

We don't currently have a way to say that native widgets implement an interface. The proposed solution is to say that CheckBox implements it, and allow NativeCheckBox to provide the implementation.

One consequence of this PR is that we must make a decision about how default property bindings are provided. The current behaviour is that the component with the implements keyword specifies the default binding. This is shown in
tests/cases/interfaces/derived_implicitly_implements.slint:

  • InterfaceWithDefaults specifies precision: 2; and confidence: 1.0;;
  • Base specifies precision; and confidence: 0.5;
  • TestCase (which has the implements keyword) specifies precision: 3;
  • TestCase expects self.precision == 3 && self.confidence == 1.0;.

Precision comes from the TestCase default whilst confidence comes from the interface default.

This behaviour is up for discussion, but largely driven by the following constraints:

  • if we specify a default in the interface, e.g. enabled: true, but don't specify anything in the implementing component, we would expect the default to match the interface. Prior to 3b04cf978 the default would have been false;
  • the current code doesn't provide a mechanism for us to check the bindings of a derived component - we can only query the property declarations.

@0x6e 0x6e enabled auto-merge (rebase) March 3, 2026 11:45
0x6e added 3 commits March 10, 2026 12:28
…ions

Previously, when a component declares that it implements an interface
the compiler would emit errors if the inherited component had
properties/callbacks/functions that collided with the interface.

Now, we check whether the implementation in the inherited component
matches that of the interface and allow the existing implementation to
be used instead.

The exception to this behaviour is how the default property bindings
are applied. The current behaviour is that the component with the
`implements` keyword specifies the default binding. This is shown in
`tests/cases/interfaces/derived_implicitly_implements.slint`:

- InterfaceWithDefaults specifies `precision: 2;` and `confidence:
  1.0;`;
- Base specifies `precision;` and `confidence: 0.5;`
- TestCase (which has the implements keyword) specifies `precision:
  3;`
- TestCase expects `self.precision == 3 && self.confidence == 1.0;`.

Precision comes from the TestCase default whilst confidence comes from
the interface default.

This behaviour is up for discussion, but largely driven by the
following constraints:
- if we specify a default in the interface, e.g. `enabled: true`, but
  don't specify anything in the implementing component, we would
expect the default to match the interface. Prior to `3b04cf978` the
default would have been `false`;
- the current code doesn't provide a mechanism for us to check the
  bindings of a derived component - we can only query the property
declarations.
…arations

Previously the compiler would emit an error if a component provided a
property or callback that matched one defined in an implemented
interface.

This change allows a component to provide an implementation of a
property or callback, as long as it matches the implementation
specified in the interface. To support this we need to interleave the
parsing of the element property declarations, bindings and callbacks
with those of the interface. This ensures that if a component provides
a default property binding for a property, but not the implementation,
the implementation is provided by the interface before the compiler
attempts to add the binding.
Don't refer to interfaces in these function names. We can use the
module name to provide context.
@0x6e 0x6e force-pushed the feature/permit-interface-implementations branch from 09c6f5a to f3c0a9c Compare March 10, 2026 12:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant