GtkFlowBox不使用水平方向在列中对齐

GtkFlowBox不使用水平方向在列中对齐,gtk,gtk3,flowlayout,Gtk,Gtk3,Flowlayout,在GTK中,我试图在一个框中对齐不同宽度的小部件,这样它们在一行中彼此相邻,如果没有更多的空间,则流入下一行。基本上,我试图得到与Qt的流布局类似的结果,如图所示: (来源:Qt文档页) 我需要每个小部件尽可能小(宽度方面),甚至只允许20像素的宽度 为了在需要更多空间时自动转到下一行,GTK提供了GtkFlowBox。我这里的问题是,它看起来像是GtkFlowBox在一个动态宽度的网格中对齐所有小部件(按单元格)。下图显示,即使非常宽的小部件也会强制其正下方的小部件使用完全相同的宽度: (

在GTK中,我试图在一个框中对齐不同宽度的小部件,这样它们在一行中彼此相邻,如果没有更多的空间,则流入下一行。基本上,我试图得到与Qt的流布局类似的结果,如图所示:

(来源:Qt文档页)

我需要每个小部件尽可能小(宽度方面),甚至只允许20像素的宽度

为了在需要更多空间时自动转到下一行,GTK提供了
GtkFlowBox
。我这里的问题是,它看起来像是
GtkFlowBox
在一个动态宽度的网格中对齐所有小部件(按单元格)。下图显示,即使非常宽的小部件也会强制其正下方的小部件使用完全相同的宽度:

(请注意,每个“…”都有自己的标签,就像每个文本序列一样。)

在图片中,第二个长标签明显导致最后一个标签获得比所需更多的空间。GTK文档并没有向我明确说明
GtkFlowBox
的工作原理类似于动态网格,但互联网上的其他图像也每次都显示网格对齐

我正在寻找一种方法,如果没有剩余空间,小部件将流入下一行,但所有行在其他方面彼此独立。Qt似乎实现了这一点,因为最后一个按钮与网格中对齐的小部件相矛盾。 是否有一种方法可以实现我正在使用
GtkFlowBox
或其他现有布局尝试的内容,或者仅通过手动实现它?我认为最简单的手动方法是使用水平
Box
es,并动态地将小部件映射到不同的框中,这样就不会使框过满。但显然,这将大大低于使用现有布局的简单性和优雅性


(请注意,如果任何人有针对特定环境编写的第三方解决方案:我正在使用Rust(Gtk rs))

我的问题的解决方案是实现我自己的容器。我想尝试记录如何处理这个问题,并在这里提供我的代码

主要概念是对容器进行子类化。使用C提供了一个很好的概述。如果您了解语言绑定中的子类化,那么可以很容易地将主要思想转移到其他语言。有两个主要问题需要解决。一个是容器所需的尺寸计算。另一个问题是子对象在容器中的实际对齐方式

关于大小计算,请求模式应设置为
HeightForWidth
,因为宽度会影响行中适合的子行数,因此也会影响需要的行数。这里的一个问题是,虽然较大的宽度意味着较小的高度,反之亦然,但gtk使用最小宽度来计算最小高度。此问题记录在中。我通过添加一个属性来解决这个问题,该属性指示一行中至少应该放置多少个孩子,这样实际的宽度就可以近似地满足可接受的高度要求。My
get_preferred_width
假设在每行中放置最小数量的子项以计算最小宽度。更自然的方法是将其设置为最大儿童的宽度,但这会导致更大的最小高度
get\u preferred\u height\u for\u width
直接取决于它所使用的分配算法,而不是真正为其子级分配大小

我在实施最小和自然尺寸的尺寸分配时遇到了一些问题。当以标准方式在儿童之间均匀使用最小高度以达到自然大小时,不可能使用剩余空间,因为儿童可能随时流向其他行,并且分布应在全球范围内公平,而不仅仅是每行。我为每个孩子指定最小宽度,并添加剩余宽度的比率,以达到所有孩子都相同的自然宽度。由于回流焊,我发现没有好的算法可以做到这一点,而不是简单的试错。因此,我只是尝试,如果可用的高度足够不同的比率,我测试这些比率下降到一个足够低的分辨率。这就产生了质量和效率之间的权衡。我使用二进制搜索来提高效率

如果您有任何具体问题,请随时提问。作为代码示例,我想提供在我的另一篇文章中建议的用户。我想提供它,因为我自己的代码已经生锈了。因此,它在思想上可能是有用的,例如,子类化与C中的gtk子类化有很多不同,这使得仅在其他语言中使用此代码非常重要。我的示例还包含一个虚拟容器,允许测试容器子对象的不同自然大小和最小大小。它是独立的。还请注意,gtk rs的最新稳定版本中不包含某些功能,请使用最新版本,例如

[dependencies.gtk]
git = "https://github.com/gtk-rs/gtk"

(另一个答案中的代码,因为答案大小有限,我现在无法共享git回购协议)

请先阅读另一个答案中的描述。由于答案长度有限,此代码被排除在外;代码和文本不能合二为一

#[macro_use]
extern crate glib;
extern crate gdk;
extern crate gio;
extern crate gtk;

use gio::prelude::*;
use glib::subclass;
use glib::subclass::prelude::*;
use glib::translate::*;
use gtk::prelude::*;

mod dummy {
    use super::*;
    glib_wrapper! {
        pub struct GAspectFillFrame(
            Object<subclass::simple::InstanceStruct<internal::GAspectFillFrame>,
            subclass::simple::ClassStruct<internal::GAspectFillFrame>,
            SimpleAppWindowClass>)
            @extends gtk::Bin, gtk::Container, gtk::Widget;

        match fn {
            get_type => || internal::GAspectFillFrame::get_type().to_glib(),
        }
    }

    impl GAspectFillFrame {
        pub fn new() -> GAspectFillFrame {
            glib::Object::new(GAspectFillFrame::static_type(), &[])
                .expect("Failed to create GAspectFillFrame instance")
                .downcast::<GAspectFillFrame>()
                .unwrap()
        }
    }

    mod internal {
        use super::*;
        use gtk::subclass::{bin::BinImpl, container::ContainerImpl, widget::WidgetImpl};
        use gtk::SizeRequestMode;

        pub struct GAspectFillFrame {}

        static PROPERTIES: [subclass::Property; 0] = [];

        impl ObjectSubclass for GAspectFillFrame {
            const NAME: &'static str = "GAspectFillFrame";
            type ParentType = gtk::Bin;
            type Instance = subclass::simple::InstanceStruct<Self>;
            type Class = subclass::simple::ClassStruct<Self>;

            glib_object_subclass!();

            fn class_init(klass: &mut Self::Class) {
                klass.install_properties(&PROPERTIES);
            }

            fn new() -> Self {
                Self {}
            }
        }

        impl ObjectImpl for GAspectFillFrame {
            fn set_property(&self, _obj: &glib::Object, _id: usize, _value: &glib::Value) {
                panic!();
            }

            fn get_property(&self, _obj: &glib::Object, _id: usize) -> Result<glib::Value, ()> {
                panic!();
            }
        }

        impl WidgetImpl for GAspectFillFrame {
            fn get_preferred_width(&self, _widget: &gtk::Widget) -> (i32, i32) {
                (50, 100)
            }

            fn get_preferred_height(&self, _widget: &gtk::Widget) -> (i32, i32) {
                (50, 100)
            }

            fn get_request_mode(&self, _widget: &gtk::Widget) -> gtk::SizeRequestMode {
                SizeRequestMode::ConstantSize
            }
        }

        impl ContainerImpl for GAspectFillFrame {}

        impl BinImpl for GAspectFillFrame {}

        impl GAspectFillFrame {}
    }
}

glib_wrapper! {
    /// A container displaying its children rowwise and dynamically reflowing children to the next row
    /// if there is no space left. In contrast to the `gtk::FlowBox` this container will not act like a grid and instead will
    /// display its rows independent of each other besides of the assignment of the children to the rows.
    ///
    /// The container allows to set the horizontal spacing between children and the vertical spacing between rows.
    ///
    /// While the height of the container is queried for given widths as an increase in the latter decreases the height,
    /// gtk uses the minimal width to calculate the minimal height. This could result in unexpected large heights.
    /// This behaviour can be controlled by influencing the minimal width such that it is reasonably high.
    /// To do so, set the `min-row-children` property. Using it to require a row to be able to contain a minimum of children which is
    /// higher than the default value `1` leads to higher minimum widths and therefore also smaller minimum heights.
    /// Note that the property is used for min width calculations but not actually enforced in size allocation.
    /// It is a soft limit to allow an easier front to back calculation for the allocation.
    /// Requiring a minimal number of children per line would imply new problems, as the minimum width calculation places a constant number
    /// of elements in one line while the real allocation does more calculations allowing a non constant number.
    /// Setting the min number of children as a hard limit could lead to situations where a for the min width calculations e.g. 4 children
    /// are in each row while for the real allocation one row may have some remaining place for one more child than necessary.
    /// In this case, the child would be in this row, the next row requires another child to reach the hard limit which could then overflow
    /// the row if it had a large size.
    /// Note that while the property is not enforced in final allocation, the min space requirements always are sufficient to place
    /// all children with their min width or larger.
    ///
    /// The container expands h-expand children with respect to the remaining space in their row which is evenly
    /// distributed among all expanding children of that row.
    ///
    /// In the case where the natural size requirements of the children cannot be satisfied, the container tries
    /// to set their size such that they get their minimal size and an additional fraction/ratio of the difference between
    /// their minimal and natural size such that this fraction is constant among all children.
    /// Note that this operation is non-trivial as changes in this ratio may lead to children being shifted into
    /// different rows and as rows may have some remaining space if others are full.
    /// The implemented solution implements a tradeoff between a visually nice rescaling behaviour and efficiency.
    pub struct ProperFlowBox(
        Object<subclass::simple::InstanceStruct<internal::ProperFlowBox>,
        subclass::simple::ClassStruct<internal::ProperFlowBox>,
        SimpleAppWindowClass>)
        @extends gtk::Container, gtk::Widget;

    match fn {
        get_type => || internal::ProperFlowBox::get_type().to_glib(),
    }
}

impl ProperFlowBox {
    /// Creates a new instance
    pub fn new() -> ProperFlowBox {
        glib::Object::new(ProperFlowBox::static_type(), &[])
            .expect("Failed to create ProperFlowBox instance")
            .downcast::<ProperFlowBox>()
            .unwrap()
    }

    /// Sets the spacing between two children in a line and between children and the container's borders
    ///
    /// # Arguments
    ///
    /// * `spacing` - Horizontal spacing in pixels
    pub fn set_h_spacing(&self, spacing: i32) {
        self.set_property("h-spacing", &spacing)
            .expect("Error setting h-spacing of ProperFlowBox");
    }

    /// Sets the spacing between two rows and between children and the container's borders
    ///
    /// # Arguments
    ///
    /// * `spacing` - Vertical spacing in pixels
    pub fn set_v_spacing(&self, spacing: i32) {
        self.set_property("v-spacing", &spacing)
            .expect("Error setting v-spacing of ProperFlowBox");
    }

    /// Sets the minimum number of children used to approximate the required width
    ///
    /// The set property is not enforced by the size allocation.
    /// Setting this to higher values may result in less required minimum height of the container.
    ///
    /// # Arguments
    ///
    /// * `min_children` - Minimum number of children per row
    pub fn set_min_row_children(&self, min_children: u32) {
        self.set_property("min-row-children", &min_children)
            .expect("Error setting min-row-children of ProperFlowBox");
    }
}

mod internal {
    use std::cell::RefCell;

    use super::*;
    use gtk::subclass::{
        container::Callback,
        container::ContainerImpl,
        widget::{WidgetImpl, WidgetImplExt},
    };

    const DEFAULT_MIN_ROW_CHILDREN: u32 = 5;

    pub struct ProperFlowBox {
        children: RefCell<Vec<gtk::Widget>>,
        h_spacing: RefCell<i32>,
        v_spacing: RefCell<i32>,
        min_row_children: RefCell<u32>,
    }

    static PROPERTIES: [subclass::Property; 3] = [
        subclass::Property("h-spacing", |h_spacing| {
            glib::ParamSpec::int(
                h_spacing,
                "Horizontal spacing",
                "Space between two children in a row and between children and the container's borders",
                0,
                i32::MAX,
                0,
                glib::ParamFlags::READWRITE,
            )
        }),
        subclass::Property("v-spacing", |v_spacing| {
            glib::ParamSpec::int(
                v_spacing,
                "Vertical spacing",
                "Space between two rows and between rows and the container's borders",
                0,
                i32::MAX,
                0,
                glib::ParamFlags::READWRITE,
            )
        }),
        subclass::Property("min-row-children", |min_row_children| {
            glib::ParamSpec::uint(
                min_row_children,
                "Minimal number of children in one row",
                "Setting this to larger numbers increases the minumum width and decreases the minimum height",
                1,
                u32::MAX,
                DEFAULT_MIN_ROW_CHILDREN,
                glib::ParamFlags::READWRITE,
            )
        }),
    ];

    impl ObjectSubclass for ProperFlowBox {
        const NAME: &'static str = "ProperFlowBox";
        type ParentType = gtk::Container;
        type Instance = subclass::simple::InstanceStruct<Self>;
        type Class = subclass::simple::ClassStruct<Self>;

        glib_object_subclass!();

        fn class_init(klass: &mut Self::Class) {
            klass.install_properties(&PROPERTIES);
        }

        fn new() -> Self {
            Self {
                children: RefCell::new(Vec::new()),
                h_spacing: RefCell::new(0),
                v_spacing: RefCell::new(0),
                min_row_children: RefCell::new(DEFAULT_MIN_ROW_CHILDREN),
            }
        }
    }

    impl ObjectImpl for ProperFlowBox {
        fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
            let prop = &PROPERTIES[id];
            match *prop {
                subclass::Property("h-spacing", ..) => {
                    *self.h_spacing.borrow_mut() = value.get_some().unwrap();
                }
                subclass::Property("v-spacing", ..) => {
                    *self.v_spacing.borrow_mut() = value.get_some().unwrap();
                }
                subclass::Property("min-row-children", ..) => {
                    *self.min_row_children.borrow_mut() = value.get_some().unwrap();
                }
                _ => panic!("Tried to set unknown property of ProperFlowBox"),
            }
        }

        fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
            let prop = &PROPERTIES[id];
            match *prop {
                subclass::Property("h-spacing", ..) => Ok(self.h_spacing.borrow().to_value()),
                subclass::Property("v-spacing", ..) => Ok(self.v_spacing.borrow().to_value()),
                subclass::Property("min-row-children", ..) => {
                    Ok(self.min_row_children.borrow().to_value())
                }
                _ => panic!("Tried to get unknown property of ProperFlowBox"),
            }
        }

        fn constructed(&self, obj: &glib::Object) {
            self.parent_constructed(obj);
            obj.downcast_ref::<gtk::Widget>()
                .unwrap()
                .set_has_window(false);
        }
    }

    impl WidgetImpl for ProperFlowBox {
        fn size_allocate(&self, widget: &gtk::Widget, allocation: &gtk::Allocation) {
            self.parent_size_allocate(widget, &allocation);

            // Search for maximal working natural ratio.
            // Sort out most likely cases (enough for 1.0 or not for more than 0.0) and do
            // binary search otherwise.
            if self.check_height_for_natural_ratio(allocation.width, 1.0, false)
                <= allocation.height
            {
                // Do 1.0
                self.check_height_for_natural_ratio(allocation.width, 1.0, true);
            } else if self.check_height_for_natural_ratio(allocation.width, 0.0, false)
                >= allocation.height
            {
                // Do 0.0
                self.check_height_for_natural_ratio(allocation.width, 0.0, true);
            } else {
                // Do binary search
                // A lower stopping eps yields higher quality by finer transitions but less performance.
                // 0.001 should be a reasonable tradeoff as binary search implies that ca. log_2(0.25/0.001) ~= 8 iterations. 

                let mut current_ratio = 0.5;
                let mut current_step_width = 0.25;
                const STOPPING_EPS: f64 = 0.001;
                let mut max_ratio: f64 = 0.0;
                while current_step_width > STOPPING_EPS {
                    let required_height =
                        self.check_height_for_natural_ratio(allocation.width, current_ratio, false);
                    if allocation.height >= required_height {
                        max_ratio = max_ratio.max(current_ratio);
                        current_ratio += current_step_width;
                    } else {
                        current_ratio -= current_step_width;
                    }
                    current_step_width /= 2.0;
                }
                self.check_height_for_natural_ratio(allocation.width, max_ratio, true);
            }
        }

        fn get_request_mode(&self, _widget: &gtk::Widget) -> gtk::SizeRequestMode {
            gtk::SizeRequestMode::HeightForWidth
        }

        fn get_preferred_height(&self, widget: &gtk::Widget) -> (i32, i32) {
            widget.get_preferred_height_for_width(widget.get_preferred_width().0)
        }

        fn get_preferred_width_for_height(&self, widget: &gtk::Widget, _height: i32) -> (i32, i32) {
            widget.get_preferred_width()
        }

        fn get_preferred_width(&self, _widget: &gtk::Widget) -> (i32, i32) {
            // Calculate an approximation of the required width by exactly placing `min_row_children` many
            // children in each row

            let mut min_width = 0;
            let mut natural_width = 0;

            let mut current_min_width = 0;
            let mut current_natural_width = 0;
            for (index, child) in self
                .children
                .borrow()
                .iter()
                .filter(|c| c.is_visible())
                .enumerate()
            {
                if index as u32 % *self.min_row_children.borrow() == 0 {
                    // Begin a new row
                    current_min_width = *self.h_spacing.borrow();
                    current_natural_width = *self.h_spacing.borrow();
                }

                current_min_width += child.get_preferred_width().0 + *self.h_spacing.borrow();
                current_natural_width += child.get_preferred_width().1 + *self.h_spacing.borrow();

                // Max each time for more consistent code as last row may not contain `min_row_children` children
                min_width = min_width.max(current_min_width);
                natural_width = natural_width.max(current_natural_width);
            }

            (min_width, natural_width)
        }

        fn get_preferred_height_for_width(&self, _widget: &gtk::Widget, width: i32) -> (i32, i32) {
            (
                self.check_height_for_natural_ratio(width, 0.0, false),
                self.check_height_for_natural_ratio(width, 1.0, false),
            )
        }
    }

    impl ContainerImpl for ProperFlowBox {
        fn add(&self, container: &gtk::Container, widget: &gtk::Widget) {
            self.children.borrow_mut().push(widget.clone());
            widget.set_parent(container);
            if container.get_visible() {
                container.queue_resize();
            }
        }

        fn remove(&self, container: &gtk::Container, widget: &gtk::Widget) {
            let index = self.children.borrow().iter().position(|c| c == widget);
            if let Some(index) = index {
                self.children.borrow_mut().remove(index);
                widget.unparent();
            } else {
                println!("Tried to remove non-child from ProperFlowBox")
            }

            if container.get_visible() {
                container.queue_resize();
            }
        }

        fn forall(
            &self,
            _container: &gtk::Container,
            _include_internals: bool,
            callback: &Callback,
        ) {
            // Need to deepcopy children as callbacks may also borrow children
            let children = (*self.children.borrow()).clone();
            for child in children.iter() {
                callback.call(child);
            }
        }
    }

    impl ProperFlowBox {
        /// Tries to fit the visible children for the given available width.
        ///
        /// Given the available width, all visible children get their minimum size plus
        /// the fraction defined by `natural_ratio` of the additional size bringing them to
        /// their natural size. The function then returns the required height for the given width.
        /// It is possible to directly call `size_allocate` for the visible children by enabling
        /// it with the corresponding parameter. Real allocating also respects the `h-expand` property
        /// of the children which does not influence the returned height of the function.
        ///
        /// # Arguments
        ///
        /// * `available_width` - Width available to be filled
        /// * `natural_ratio` - Fraction of the additional size to meet the natural size coming from the minimum size.
        ///                     For a ratio `x` the allocated width and height will be `min + x * (max - min) == (1 - x) * min + x * max`
        /// * `allocate` - Call `size-allocate` on visible children if true
        fn check_height_for_natural_ratio(
            &self,
            available_width: i32,
            natural_ratio: f64,
            allocate: bool,
        ) -> i32 {
            // Coordinates of next child
            let mut x = *self.h_spacing.borrow();
            let mut y = *self.v_spacing.borrow();

            let mut line_height = 0;
            let mut number_row_children = 0;
            let mut number_hexpand_row_children = 0;
            let mut row_start_index = 0;
            for (index, child) in self
                .children
                .borrow()
                .iter()
                .enumerate()
                .filter(|(_, c)| c.is_visible())
            {
                let width = ProperFlowBox::get_barycentric_combination(
                    child.get_preferred_width(),
                    natural_ratio,
                );
                let height = ProperFlowBox::get_barycentric_combination(
                    match child.get_request_mode() {
                        gtk::SizeRequestMode::ConstantSize => child.get_preferred_height(),
                        gtk::SizeRequestMode::HeightForWidth
                        | gtk::SizeRequestMode::WidthForHeight => {
                            child.get_preferred_height_for_width(width)
                        }
                        _ => panic!("Unknown size request mode"),
                    },
                    natural_ratio,
                );
                if number_row_children > 0 && x + width + *self.h_spacing.borrow() > available_width
                {
                    // Not enough space in current line => Go to next line
                    // Exception: Current child will be only one in line, then assign as there is no
                    // valid assignment to any line for this child

                    // Allocate finished line
                    if allocate {
                        self.allocate_row(
                            &self.children.borrow()[row_start_index..index],
                            y,
                            line_height,
                            number_hexpand_row_children,
                            available_width - x,
                            natural_ratio,
                        );
                    }

                    // Start next line
                    x = *self.h_spacing.borrow();
                    y += line_height + *self.v_spacing.borrow();
                    line_height = 0;
                    number_row_children = 0;
                    number_hexpand_row_children = 0;
                    row_start_index = index;
                }
                line_height = line_height.max(height);
                x += width + *self.h_spacing.borrow();
                number_row_children += 1;
                if child.get_hexpand() {
                    number_hexpand_row_children += 1;
                }
            }

            // Allocate last line
            if allocate {
                self.allocate_row(
                    &self.children.borrow()[row_start_index..],
                    y,
                    line_height,
                    number_hexpand_row_children,
                    available_width - x,
                    natural_ratio,
                );
            }

            y + line_height + *self.v_spacing.borrow()
        }

        /// Allocates the size for the children in the given slice
        ///
        /// # Arguments
        ///
        /// * `children` - Slice of which the visible children inside form a row
        /// * `y` - y coordinate of the row
        /// * `height` - height of the row
        /// * `number_hexpand_children` - Number of children with h-expand
        /// * `remaining_space` - Unneeded space to be distributed among h-expand children
        /// * `natural_ratio` - Fraction of the additional size to meet the natural size coming from the minimum size.
        ///                     For a ratio `x` the allocated width and height will be `min + x * (max - min) == (1 - x) * min + x * max`
        fn allocate_row(
            &self,
            children: &[gtk::Widget],
            y: i32,
            height: i32,
            number_hexpand_children: i32,
            remaining_space: i32,
            natural_ratio: f64,
        ) {
            let mut x = *self.h_spacing.borrow();
            let additional_width_per_child = if number_hexpand_children > 0 {
                remaining_space / number_hexpand_children
            } else {
                0
            };
            for child in children.iter().filter(|c| c.is_visible()) {
                let mut width = ProperFlowBox::get_barycentric_combination(
                    child.get_preferred_width(),
                    natural_ratio,
                );
                if child.get_hexpand() {
                    width += additional_width_per_child;
                }
                child.size_allocate(&gtk::Allocation {
                    x,
                    y,
                    width,
                    height,
                });
                x += width + *self.h_spacing.borrow();
            }
        }

        /// Returns the barycentric combination of `min` and `max` with the given ratio,
        /// namely `min + ratio * (max - min) == (1 - ratio) * min + ratio * max`
        ///
        /// # Arguments
        ///
        /// * `min` - Min value
        /// * `max` - Max value
        /// * `ratio` - barycentric parameter, should be in `[0,1]` such that result is in `[min,max]`
        fn get_barycentric_combination((min, max): (i32, i32), ratio: f64) -> i32 {
            ((1.0 - ratio) * min as f64 + ratio * max as f64) as i32
        }
    }
}

fn main() {
    let application = gtk::Application::new(None, Default::default())
        .expect("Failed to initialize GTK application");

    application.connect_activate(|app| {
        let window = gtk::ApplicationWindow::new(app);
        window.set_title("Custom FlowBox Demo");
        let style = ".bordered {border: 1px solid black;}";
        let provider = gtk::CssProvider::new();
        provider
            .load_from_data(style.as_bytes())
            .expect("Failed to load CSS");
        gtk::StyleContext::add_provider_for_screen(
            &gdk::Screen::get_default().expect("Failed to load css provider"),
            &provider,
            gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
        );

        let flow = ProperFlowBox::new();
        flow.set_h_spacing(10);
        flow.set_v_spacing(10);

        for i in 1..15 {
            let dummy = dummy::GAspectFillFrame::new();
            let label = gtk::Label::new(Some("Hello world"));
            dummy.add(&label);
            if i % 3 == 0 {
                dummy.set_hexpand(true);
            }
            label.get_style_context().add_class("bordered");
            flow.add(&dummy);
        }

        window.add(&flow);
        window.show_all();
    });

    application.run(&[]);
}
#[宏使用]
外部板条箱油嘴滑舌;
外部板条箱gdk;
外部板条箱gio;
外部板条箱gtk;
使用gio::前奏::*;
使用glib::subclass;
使用glib::subclass::prelude::*;
使用glib::translate::*;
使用gtk::前奏::*;
模仿者{
使用超级::*;
油嘴滑舌的包装{
pub结构GAspectFillFrame(
(对象)
@扩展gtk::Bin、gtk::Container、gtk::Widget;
匹配fn{
get_type=>||internal::GAspectFillFrame::get_type()。to_glib(),
}
}
impl GAspectFillFrame{
pub fn new()->GAspectFillFrame{
glib::Object::new(GAspectFillFrame::static_type(),&[]
.expect(“未能创建GAspectFillFrame实例”)
.沮丧::()
.unwrap()