Hey guys, it's been a while since my last topic.
I'm trying to create a horizontal split container with fltk-rs, everything works fine, you can set whether the child in the container is fixed-width or scalable, and set a minimum width for each child, and the splitters can be moved to change the width of the children, however, after moving the splitters, when I change the size of the window, I find that all the splitters have returned to their original positions as if I had not moved any of them.
my_fltk_project/
├── src/
│ ├── layouts/
│ │ └── h_split_container.rs
│ └── main.rs
├── Cargo.toml
Cargo.toml
[package]
name = "my_fltk_project"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
fltk = "1.4.32"
h_split_container.rs:
use fltk::{
app,
button::Button,
enums::{Color, Event, FrameType},
group::Group,
prelude::*,
widget::Widget,
};
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Clone)]
struct Child {
widget: Rc<RefCell<dyn WidgetExt>>,
min_width: i32,
fixed: bool,
last_width: i32,
}
pub struct HSplitContainer {
group: Group,
children: Vec<Child>,
splitters: Vec<Button>,
last_width: i32,
}
impl HSplitContainer {
pub fn new(x: i32, y: i32, width: i32, height: i32) -> Self {
let mut group = Group::new(x, y, width, height, "");
group.set_frame(FrameType::FlatBox);
HSplitContainer {
group,
children: Vec::new(),
splitters: Vec::new(),
last_width: width,
}
}
pub fn add_child<W: WidgetExt + Clone + 'static>(&mut self, child: W, min_width: i32, fixed: bool) {
let child_ref = Rc::new(RefCell::new(child.clone()));
self.children.push(Child {
widget: child_ref.clone(),
min_width,
fixed,
last_width: min_width, // Initialize last_width with min_width
});
self.group.add(&child);
if self.children.len() > 1 {
self.add_splitter();
}
self.layout_children();
}
fn add_splitter(&mut self) {
let mut splitter = Button::new(0, 0, 5, self.group.height(), "|");
splitter.set_frame(FrameType::FlatBox);
splitter.set_color(Color::Red);
let mut group_clone = self.group.clone();
let index = self.splitters.len(); // Current splitter index
let children_clone = Rc::new(RefCell::new(self.children.clone()));
splitter.handle({
let children_clone = Rc::clone(&children_clone);
move |s, ev| match ev {
Event::Push => true,
Event::Drag => {
let mut children = children_clone.borrow_mut();
if children[index].fixed || children[index + 1].fixed {
return true;
}
let diff = app::event_x() - s.x();
let new_x = s.x() + diff;
// Calculate minimum and maximum x positions for the splitter
let prev_child_pos = children[index].widget.borrow().x();
let prev_child_width = children[index].widget.borrow().width();
let next_child_pos = children[index + 1].widget.borrow().x();
let next_child_width = children[index + 1].widget.borrow().width();
let min_x = prev_child_pos + children[index].min_width;
let max_x = next_child_pos + next_child_width - children[index + 1].min_width - s.width();
// Ensure that moving the splitter maintains the minimum widths of both children
if new_x >= min_x && new_x <= max_x {
let new_first_child_width = new_x - prev_child_pos;
let new_second_child_pos = new_x + s.width();
let new_second_child_width = next_child_pos + next_child_width - new_second_child_pos;
if new_first_child_width >= children[index].min_width && new_second_child_width >= children[index + 1].min_width {
s.set_pos(new_x, s.y());
// Apply changes to children
children[index].widget.borrow_mut().set_size(new_first_child_width, group_clone.height());
children[index + 1].widget.borrow_mut().set_pos(new_second_child_pos, 0);
children[index + 1].widget.borrow_mut().set_size(new_second_child_width, group_clone.height());
children[index].last_width = new_first_child_width; // Update last_width
children[index + 1].last_width = new_second_child_width; // Update last_width
println!("Splitter moved: {:?}", s.x());
println!("First child width: {:?}", new_first_child_width);
println!("Second child width: {:?}", new_second_child_width);
group_clone.redraw();
}
}
true
}
_ => false,
}
});
self.group.add(&splitter);
self.splitters.push(splitter);
self.layout_children();
}
fn layout_children(&mut self) {
let num_children = self.children.len();
if num_children > 1 {
let splitter_width = 5;
let mut fixed_width = 0;
let mut resizable_count = 0;
for child in &self.children {
if child.fixed {
fixed_width += child.min_width;
} else {
resizable_count += 1;
}
}
let available_width = self.group.width() - fixed_width - (splitter_width * (num_children - 1) as i32);
let resizable_width = if resizable_count > 0 {
available_width / resizable_count as i32
} else {
0
};
let mut x_offset = 0;
for (index, child) in self.children.iter_mut().enumerate() {
let mut child_ref = child.widget.borrow_mut();
if child.fixed {
child_ref.set_pos(x_offset, 0);
child_ref.set_size(child.min_width, self.group.height());
child.last_width = child.min_width; // Ensure last_width is updated
x_offset += child.min_width;
} else {
let child_width = if resizable_width >= child.min_width {
resizable_width
} else {
child.min_width
};
child_ref.set_pos(x_offset, 0);
child_ref.set_size(child_width, self.group.height());
child.last_width = child_width; // Ensure last_width is updated
x_offset += child_width;
}
println!("Child {} width set to: {}", index, child_ref.width());
if index < num_children - 1 {
if let Some(splitter_ref) = self.splitters.get_mut(index) {
splitter_ref.set_pos(x_offset, 0);
splitter_ref.set_size(splitter_width, self.group.height());
x_offset += splitter_width;
}
}
child_ref.redraw();
}
} else if num_children == 1 {
if let Some(child) = self.children.get_mut(0) {
let mut child_ref = child.widget.borrow_mut();
child_ref.set_pos(0, 0);
child_ref.set_size(self.group.width(), self.group.height());
child.last_width = self.group.width(); // Ensure last_width is updated
println!("Single child width set to: {}", self.group.width());
child_ref.redraw();
}
}
self.group.redraw();
}
pub fn group(&self) -> &Group {
&self.group
}
pub fn set_resizable(&mut self) {
self.group.set_frame(FrameType::FlatBox);
self.group.set_color(Color::White);
self.group.resizable(&self.group);
}
}
main.rs:
use fltk::{app, prelude::*, window::Window};
mod layouts;
use crate::layouts::h_split_container::HSplitContainer;
fn main() {
let app = app::App::default().with_scheme(app::AppScheme::Gleam);
let mut wind = Window::new(100, 100, 800, 600, "HSplitContainer Example");
let mut h_split = HSplitContainer::new(0, 0, 800, 600);
h_split.add_child(fltk::frame::Frame::new(0, 0, 200, 600, "Child 1"), 100,true); // Fixed width
h_split.add_child(fltk::frame::Frame::new(0, 0, 200, 600, "Child 2"), 100, false);
h_split.add_child(fltk::frame::Frame::new(0, 0, 200, 600, "Child 3"), 100, false);
h_split.add_child(fltk::frame::Frame::new(0, 0, 200, 600, "Child 4"), 100, false);
wind.end();
wind.make_resizable(true);
wind.show();
app.run().unwrap();
}