Skip to content

compiler: Have a key-sequence type #8906

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion internal/compiler/builtin_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,12 @@ fn to_debug_string(
Type::Float32 | Type::Int32 => expr.maybe_convert_to(Type::String, node, diag),
Type::String => expr,
// TODO
Type::Color | Type::Brush | Type::Image | Type::Easing | Type::Array(_) => {
Type::Color
| Type::Brush
| Type::Image
| Type::Easing
| Type::Array(_)
| Type::KeyboardShortcut => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tired to return a String that contains the stringified definition, but that causes infinite recursion... I need to check out why that happens in detail.

Expression::StringLiteral("<debug-of-this-type-not-yet-implemented>".into())
}
Type::Duration
Expand Down
16 changes: 15 additions & 1 deletion internal/compiler/expression_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

use crate::diagnostics::{BuildDiagnostics, SourceLocation, Spanned};
use crate::langtype::{BuiltinElement, EnumerationValue, Function, Struct, Type};
use crate::langtype::{BuiltinElement, EnumerationValue, Function, KeyboardShortcut, Struct, Type};
use crate::layout::Orientation;
use crate::lookup::LookupCtx;
use crate::object_tree::*;
Expand Down Expand Up @@ -711,6 +711,8 @@ pub enum Expression {

EnumerationValue(EnumerationValue),

KeyboardShortcut(Vec<KeyboardShortcut>),

ReturnStatement(Option<Box<Expression>>),

LayoutCacheAccess {
Expand Down Expand Up @@ -851,6 +853,7 @@ impl Expression {
Expression::LinearGradient { .. } => Type::Brush,
Expression::RadialGradient { .. } => Type::Brush,
Expression::EnumerationValue(value) => Type::Enumeration(value.enumeration.clone()),
Expression::KeyboardShortcut(_) => Type::KeyboardShortcut,
// invalid because the expression is unreachable
Expression::ReturnStatement(_) => Type::Invalid,
Expression::LayoutCacheAccess { .. } => Type::LogicalLength,
Expand Down Expand Up @@ -940,6 +943,7 @@ impl Expression {
}
}
Expression::EnumerationValue(_) => {}
Expression::KeyboardShortcut(_) => {}
Expression::ReturnStatement(expr) => {
expr.as_deref().map(visitor);
}
Expand Down Expand Up @@ -1037,6 +1041,7 @@ impl Expression {
}
}
Expression::EnumerationValue(_) => {}
Expression::KeyboardShortcut(_) => {}
Expression::ReturnStatement(expr) => {
expr.as_deref_mut().map(visitor);
}
Expand Down Expand Up @@ -1123,6 +1128,7 @@ impl Expression {
stops.iter().all(|(c, s)| c.is_constant() && s.is_constant())
}
Expression::EnumerationValue(_) => true,
Expression::KeyboardShortcut(_) => true,
Expression::ReturnStatement(expr) => {
expr.as_ref().map_or(true, |expr| expr.is_constant())
}
Expand Down Expand Up @@ -1365,6 +1371,7 @@ impl Expression {
Type::Enumeration(enumeration) => {
Expression::EnumerationValue(enumeration.clone().default_value())
}
Type::KeyboardShortcut => Expression::KeyboardShortcut(vec![]),
Type::ComponentFactory => Expression::EmptyComponentFactory,
}
}
Expand Down Expand Up @@ -1745,6 +1752,13 @@ pub fn pretty_print(f: &mut dyn std::fmt::Write, expression: &Expression) -> std
Some(val) => write!(f, "{}.{}", e.enumeration.name, val),
None => write!(f, "{}.{}", e.enumeration.name, e.value),
},
Expression::KeyboardShortcut(ks) => {
if ks.is_empty() {
write!(f, "@keys()")
} else {
todo!()
}
}
Expression::ReturnStatement(e) => {
write!(f, "return ")?;
e.as_ref().map(|e| pretty_print(f, e)).unwrap_or(Ok(()))
Expand Down
16 changes: 8 additions & 8 deletions internal/compiler/generator/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ pub mod cpp_ast {

use smol_str::{format_smolstr, SmolStr};

thread_local!(static INDENTATION : Cell<u32> = Cell::new(0));
thread_local!(static INDENTATION : Cell<u32> = const { Cell::new(0) });
fn indent(f: &mut Formatter<'_>) -> Result<(), Error> {
INDENTATION.with(|i| {
for _ in 0..(i.get()) {
Expand Down Expand Up @@ -167,7 +167,7 @@ pub mod cpp_ast {
Some(Declaration::Var(Var {
ty: var.ty.clone(),
name: var.name.clone(),
array_size: var.array_size.clone(),
array_size: var.array_size,
init: std::mem::take(&mut var.init),
is_extern: false,
..Default::default()
Expand Down Expand Up @@ -506,7 +506,7 @@ impl CppType for Type {
Type::Void => Some("void".into()),
Type::Float32 => Some("float".into()),
Type::Int32 => Some("int".into()),
Type::String => Some("slint::SharedString".into()),
Type::String | Type::KeyboardShortcut => Some("slint::SharedString".into()),
Type::Color => Some("slint::Color".into()),
Type::Duration => Some("std::int64_t".into()),
Type::Angle => Some("float".into()),
Expand Down Expand Up @@ -560,7 +560,7 @@ fn remove_parentheses(expr: &str) -> &str {
if expr.starts_with('(') && expr.ends_with(')') {
let mut level = 0;
// check that the opening and closing parentheses are on the same level
for byte in expr[1..expr.len() - 1].as_bytes() {
for byte in &expr.as_bytes()[1..expr.len() - 1] {
match byte {
b')' if level == 0 => return expr,
b')' => level -= 1,
Expand Down Expand Up @@ -873,7 +873,7 @@ pub fn generate(

for (cpp_file_name, cpp_file) in config.cpp_files.iter().zip(cpp_files) {
use std::io::Write;
write!(&mut BufWriter::new(std::fs::File::create(&cpp_file_name)?), "{cpp_file}")?;
write!(&mut BufWriter::new(std::fs::File::create(cpp_file_name)?), "{cpp_file}")?;
}

Ok(file)
Expand Down Expand Up @@ -2220,7 +2220,7 @@ fn generate_sub_component(
" if (!self->{name}.running() || self->{name}.interval() != interval)"
));
update_timers.push(format!(" self->{name}.start(slint::TimerMode::Repeated, interval, [self] {{ {callback}; }});"));
update_timers.push(format!("}} else {{ self->{name}.stop(); }}").into());
update_timers.push(format!("}} else {{ self->{name}.stop(); }}"));
target_struct.members.push((
field_access,
Declaration::Var(Var { ty: "slint::Timer".into(), name, ..Default::default() }),
Expand Down Expand Up @@ -3131,7 +3131,7 @@ fn compile_expression(expr: &llr::Expression, ctx: &EvaluationContext) -> String
let item_rc = access_item_rc(function, ctx);
let window = access_window_field(ctx);
let (native, name) = native_prop_info(function, ctx);
let function_name = format!("slint_{}_{}", native.class_name.to_lowercase(), ident(&name).to_lowercase());
let function_name = format!("slint_{}_{}", native.class_name.to_lowercase(), ident(name).to_lowercase());
format!("{function_name}(&{item}, &{window}.handle(), &{item_rc})")
}
Expression::ExtraBuiltinFunctionCall { function, arguments, return_ty: _ } => {
Expand Down Expand Up @@ -4002,7 +4002,7 @@ fn compile_builtin_function_call(
"self->update_timers()".into()
}
BuiltinFunction::DetectOperatingSystem => {
format!("slint::cbindgen_private::slint_detect_operating_system()")
"slint::cbindgen_private::slint_detect_operating_system()".to_string()
}

}
Expand Down
2 changes: 1 addition & 1 deletion internal/compiler/generator/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ pub fn rust_primitive_type(ty: &Type) -> Option<proc_macro2::TokenStream> {
Type::Void => Some(quote!(())),
Type::Int32 => Some(quote!(i32)),
Type::Float32 => Some(quote!(f32)),
Type::String => Some(quote!(sp::SharedString)),
Type::String | Type::KeyboardShortcut => Some(quote!(sp::SharedString)),
Type::Color => Some(quote!(sp::Color)),
Type::ComponentFactory => Some(quote!(slint::ComponentFactory)),
Type::Duration => Some(quote!(i64)),
Expand Down
40 changes: 40 additions & 0 deletions internal/compiler/langtype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pub enum Type {
Array(Rc<Type>),
Struct(Rc<Struct>),
Enumeration(Rc<Enumeration>),
KeyboardShortcut,

/// A type made up of the product of several "unit" types.
/// The first parameter is the unit, and the second parameter is the power.
Expand Down Expand Up @@ -101,6 +102,7 @@ impl core::cmp::PartialEq for Type {
matches!(other, Type::Struct(rhs) if lhs.fields == rhs.fields && lhs.name == rhs.name)
}
Type::Enumeration(lhs) => matches!(other, Type::Enumeration(rhs) if lhs == rhs),
Type::KeyboardShortcut => matches!(other, Type::KeyboardShortcut),
Type::UnitProduct(a) => matches!(other, Type::UnitProduct(b) if a == b),
Type::ElementReference => matches!(other, Type::ElementReference),
Type::LayoutCache => matches!(other, Type::LayoutCache),
Expand Down Expand Up @@ -160,6 +162,7 @@ impl Display for Type {
Type::Easing => write!(f, "easing"),
Type::Brush => write!(f, "brush"),
Type::Enumeration(enumeration) => write!(f, "enum {}", enumeration.name),
Type::KeyboardShortcut => write!(f, "keyboard-shortcut"),
Type::UnitProduct(vec) => {
const POWERS: &[char] = &['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹'];
let mut x = vec.iter().map(|(unit, power)| {
Expand Down Expand Up @@ -208,6 +211,7 @@ impl Type {
| Self::Bool
| Self::Easing
| Self::Enumeration(_)
| Self::KeyboardShortcut
| Self::ElementReference
| Self::Struct { .. }
| Self::Array(_)
Expand Down Expand Up @@ -265,6 +269,7 @@ impl Type {
| (Type::LogicalLength, Type::Rem)
| (Type::PhysicalLength, Type::Rem)
| (Type::Percent, Type::Float32)
| (Type::KeyboardShortcut, Type::String)
| (Type::Brush, Type::Color)
| (Type::Color, Type::Brush) => true,
(Type::Array(a), Type::Model) if a.is_property_type() => true,
Expand Down Expand Up @@ -311,6 +316,7 @@ impl Type {
Type::Array(_) => None,
Type::Struct { .. } => None,
Type::Enumeration(_) => None,
Type::KeyboardShortcut => None,
Type::UnitProduct(_) => None,
Type::ElementReference => None,
Type::LayoutCache => None,
Expand Down Expand Up @@ -839,6 +845,40 @@ impl Enumeration {
}
}

#[derive(Clone, Debug, Default)]
pub struct KeyboardModifiers {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't we already have such a type somewhere else?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do. But I can not get to it from here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do have this type in i_slint_core, but that is not listed in the compiler's Cargo.toml. I did not want to add it, as there is probably a reason for not having that.

pub alt: bool,
pub control: bool,
pub meta: bool,
pub shift: bool,
}

#[derive(Clone, Debug, Default)]
pub struct KeyboardShortcut {
pub key: char,
pub modifiers: KeyboardModifiers,
}

impl PartialEq for KeyboardShortcut {
fn eq(&self, _other: &Self) -> bool {
true
}
}

impl std::fmt::Display for KeyboardShortcut {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let alt = if self.modifiers.alt { "alt+" } else { "" };
let ctrl = if self.modifiers.control { "ctrl+" } else { "" };
let meta = if self.modifiers.meta { "meta+" } else { "" };
let shift = if self.modifiers.shift { "shift+" } else { "" };
write!(f, "{alt}{ctrl}{meta}{shift}{}", self.key)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking we also want to support key that are not printable like arrows, enter, media keys, F keys and so on.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that will be needed.

}
}

pub fn keyboard_shortcuts_to_string(shortcuts: &[KeyboardShortcut]) -> String {
shortcuts.iter().map(|ks| ks.to_string()).join(", ")
}

#[derive(Clone, Debug)]
pub struct EnumerationValue {
pub value: usize, // index in enumeration.values
Expand Down
1 change: 1 addition & 0 deletions internal/compiler/llr/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ impl Expression {
Type::Enumeration(enumeration) => {
Expression::EnumerationValue(enumeration.clone().default_value())
}
Type::KeyboardShortcut => Expression::StringLiteral(SmolStr::new_static("")),
Type::ComponentFactory => Expression::EmptyComponentFactory,
})
}
Expand Down
5 changes: 4 additions & 1 deletion internal/compiler/llr/lower_expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use smol_str::{format_smolstr, SmolStr};
use super::lower_to_item_tree::{LoweredElement, LoweredSubComponentMapping, LoweringState};
use super::{Animation, PropertyIdx, PropertyReference, RepeatedElementIdx};
use crate::expression_tree::{BuiltinFunction, Callable, Expression as tree_Expression};
use crate::langtype::{EnumerationValue, Struct, Type};
use crate::langtype::{self, EnumerationValue, Struct, Type};
use crate::layout::Orientation;
use crate::llr::Expression as llr_Expression;
use crate::namedreference::NamedReference;
Expand Down Expand Up @@ -233,6 +233,9 @@ pub fn lower_expression(
.collect::<_>(),
},
tree_Expression::EnumerationValue(e) => llr_Expression::EnumerationValue(e.clone()),
tree_Expression::KeyboardShortcut(ks) => {
llr_Expression::StringLiteral(SmolStr::from(langtype::keyboard_shortcuts_to_string(ks)))
}
tree_Expression::ReturnStatement(..) => {
panic!("The remove return pass should have removed all return")
}
Expand Down
4 changes: 3 additions & 1 deletion internal/compiler/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ declare_syntax! {
Expression-> [ ?Expression, ?FunctionCallExpression, ?IndexExpression, ?SelfAssignment,
?ConditionalExpression, ?QualifiedName, ?BinaryExpression, ?Array, ?ObjectLiteral,
?UnaryOpExpression, ?CodeBlock, ?StringTemplate, ?AtImageUrl, ?AtGradient, ?AtTr,
?MemberAccess ],
?MemberAccess, ?AtKeys ],
/// Concatenate the Expressions to make a string (usually expended from a template string)
StringTemplate -> [*Expression],
/// `@image-url("foo.png")`
Expand All @@ -387,6 +387,8 @@ declare_syntax! {
TrContext -> [],
/// `| "foo" % n` in a `AtTr` node
TrPlural -> [Expression],
/// `@keys(...)`
AtKeys -> [],
/// expression()
FunctionCallExpression -> [*Expression],
/// `expression[index]`
Expand Down
Loading
Loading