Phase 4: Steuerelemente implementieren

This commit is contained in:
2026-09-05 12:49:46 +02:00
parent 1cb6ae8fd9
commit 19804e0e2d
36 changed files with 6472 additions and 689 deletions

View File

@@ -21,10 +21,11 @@ pub enum ObjectClass {
Timer,
VScrollBar,
Screen,
Spin,
}
impl ObjectClass {
pub const ALL: [Self; 18] = [
pub const ALL: [Self; 19] = [
Self::Form,
Self::CheckBox,
Self::ComboBox,
@@ -43,6 +44,7 @@ impl ObjectClass {
Self::Timer,
Self::VScrollBar,
Self::Screen,
Self::Spin,
];
pub fn name(self) -> &'static str {
@@ -65,6 +67,7 @@ impl ObjectClass {
Self::Timer => "TIMER",
Self::VScrollBar => "VSCROLLBAR",
Self::Screen => "SCREEN",
Self::Spin => "SPIN",
}
}
@@ -88,6 +91,7 @@ impl ObjectClass {
Self::Timer => "Timer",
Self::VScrollBar => "VScrollBar",
Self::Screen => "Screen",
Self::Spin => "Spin",
}
}
@@ -295,7 +299,7 @@ pub fn properties(class: ObjectClass) -> Vec<PropertySpec> {
string("TEXT", ""),
]),
CheckBox => p.extend([string("CAPTION", ""), range("VALUE", 0, 0, 2)]),
OptionButton => p.extend([string("CAPTION", ""), range("VALUE", 0, -1, 0)]),
OptionButton => p.extend([string("CAPTION", ""), range("VALUE", 0, -1, 1)]),
Frame => p.push(string("CAPTION", "")),
Label => p.extend([
range("ALIGNMENT", 0, 0, 2),
@@ -311,6 +315,21 @@ pub fn properties(class: ObjectClass) -> Vec<PropertySpec> {
range("SMALLCHANGE", 1, 1, 32767),
range("VALUE", 0, -32768, 32767),
]),
Spin => {
p.extend([
ro("BORDERSTYLE", PropertyType::Integer),
range("INTERVAL", 250, 0, 65535),
range("MIN", 0, -32768, 32767),
range("MAX", 32767, -32768, 32767),
range("STYLE", 0, 0, 1),
range("VALUE", 0, -32768, 32767),
]);
for name in ["HEIGHT", "WIDTH"] {
if let Some(property) = p.iter_mut().find(|property| property.name == name) {
property.writable = false;
}
}
}
PictureBox => p.extend([
boolp("AUTOREDRAW", false),
range("BORDERSTYLE", 1, 0, 2),
@@ -337,10 +356,26 @@ pub fn properties(class: ObjectClass) -> Vec<PropertySpec> {
boolp("SEPARATOR", false),
string("TAG", ""),
boolp("VISIBLE", true),
string("SHORTCUT", ""),
]),
DirListBox => p.extend([
ro("LIST", PropertyType::String),
ro("LISTCOUNT", PropertyType::Integer),
int("LISTINDEX", -1),
string("PATH", ""),
string("TEXT", ""),
]),
DriveListBox => p.extend([
ro("LIST", PropertyType::String),
ro("LISTCOUNT", PropertyType::Integer),
int("LISTINDEX", -1),
string("DRIVE", ""),
string("TEXT", ""),
]),
DirListBox => p.extend([string("PATH", ""), string("TEXT", "")]),
DriveListBox => p.extend([string("DRIVE", ""), string("TEXT", "")]),
FileListBox => p.extend([
ro("LIST", PropertyType::String),
ro("LISTCOUNT", PropertyType::Integer),
int("LISTINDEX", -1),
string("FILENAME", ""),
string("PATH", ""),
string("PATTERN", "*.*"),
@@ -413,6 +448,7 @@ pub fn methods(class: ObjectClass) -> &'static [&'static str] {
&["DRAG", "MOVE", "REFRESH", "SETFOCUS"]
}
Frame | Label | HScrollBar | VScrollBar => &["DRAG", "MOVE", "REFRESH"],
Spin => &["DRAG", "REFRESH", "SETFOCUS"],
PictureBox => &[
"CLS",
"DRAG",
@@ -429,11 +465,7 @@ pub fn methods(class: ObjectClass) -> &'static [&'static str] {
}
pub fn method_is_implemented(class: ObjectClass, name: &str) -> bool {
matches!(
(class, name),
(ObjectClass::Form, "HIDE" | "LOAD" | "SHOW" | "UNLOAD")
| (ObjectClass::Screen, "HIDE" | "SHOW")
)
methods(class).contains(&name)
}
pub fn method_arity(class: ObjectClass, name: &str) -> Option<(usize, usize)> {
@@ -441,6 +473,22 @@ pub fn method_arity(class: ObjectClass, name: &str) -> Option<(usize, usize)> {
(ObjectClass::Form, "SHOW") => Some((0, 1)),
(ObjectClass::Form, "HIDE" | "LOAD" | "UNLOAD")
| (ObjectClass::Screen, "HIDE" | "SHOW") => Some((0, 0)),
(_, "REFRESH" | "SETFOCUS" | "CLS" | "PRINTFORM") => Some((0, 0)),
(_, "DRAG") => Some((1, 1)),
(_, "MOVE") => Some((2, 4)),
(ObjectClass::ListBox | ObjectClass::ComboBox, "ADDITEM") => Some((1, 2)),
(ObjectClass::ListBox | ObjectClass::ComboBox, "REMOVEITEM") => Some((1, 1)),
(ObjectClass::Form | ObjectClass::PictureBox, "PRINT") => Some((0, usize::MAX)),
(ObjectClass::Form | ObjectClass::PictureBox, "TEXTWIDTH" | "TEXTHEIGHT") => Some((1, 1)),
_ => None,
}
}
pub fn method_return_type(class: ObjectClass, name: &str) -> Option<PropertyType> {
match (class, name) {
(ObjectClass::Form | ObjectClass::PictureBox, "TEXTWIDTH" | "TEXTHEIGHT") => {
Some(PropertyType::Integer)
}
_ => None,
}
}
@@ -558,6 +606,16 @@ pub fn events(class: ObjectClass) -> &'static [&'static str] {
"KEYUP",
"LOSTFOCUS",
],
Spin => &[
"CUSTOM",
"DRAGDROP",
"DRAGOVER",
"GOTFOCUS",
"KEYDOWN",
"KEYPRESS",
"KEYUP",
"LOSTFOCUS",
],
PictureBox => &[
"CLICK",
"DBLCLICK",
@@ -608,6 +666,7 @@ pub fn event_params(event: &str) -> Option<&'static [(&'static str, EventParamTy
("STATE", Integer),
],
"UNLOAD" => &[("CANCEL", Integer)],
"CUSTOM" => &[("EVENTTYPE", Integer)],
"CLICK" | "DBLCLICK" | "CHANGE" | "DROPDOWN" | "GOTFOCUS" | "LOSTFOCUS" | "LOAD"
| "PAINT" | "RESIZE" | "TIMER" | "PATHCHANGE" | "PATTERNCHANGE" => &[],
_ => return None,
@@ -652,4 +711,18 @@ impl FormCatalog {
.find(|(_, o)| o.name.eq_ignore_ascii_case(name))
.map(|(i, o)| (i as u16, o))
}
pub fn belongs_to(&self, object: &FormObject, form: &str) -> bool {
let mut parent = object.parent_form.as_deref();
for _ in 0..self.objects.len() {
let Some(name) = parent else { return false };
if name.eq_ignore_ascii_case(form) {
return true;
}
parent = self
.find(name)
.and_then(|(_, object)| object.parent_form.as_deref());
}
false
}
}