mirror of
https://github.com/danbulant/cushy
synced 2026-07-07 12:10:44 +00:00
Renamed Array to Stack
This commit is contained in:
parent
c9c4c9aeed
commit
93a9545cc4
5 changed files with 70 additions and 71 deletions
|
|
@ -1,14 +1,14 @@
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
|
|
||||||
use gooey::value::Dynamic;
|
use gooey::value::Dynamic;
|
||||||
use gooey::widgets::array::Array;
|
use gooey::widgets::stack::Stack;
|
||||||
use gooey::widgets::{Button, Label};
|
use gooey::widgets::{Button, Label};
|
||||||
use gooey::{widgets, Run};
|
use gooey::{widgets, Run};
|
||||||
|
|
||||||
fn main() -> gooey::Result {
|
fn main() -> gooey::Result {
|
||||||
let counter = Dynamic::new(0i32);
|
let counter = Dynamic::new(0i32);
|
||||||
let label = counter.map_each(ToString::to_string);
|
let label = counter.map_each(ToString::to_string);
|
||||||
Array::rows(widgets![
|
Stack::rows(widgets![
|
||||||
Label::new(label),
|
Label::new(label),
|
||||||
Button::new("+").on_click(counter.with_clone(|counter| {
|
Button::new("+").on_click(counter.with_clone(|counter| {
|
||||||
move |_| {
|
move |_| {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use gooey::styles::components::TextColor;
|
use gooey::styles::components::TextColor;
|
||||||
use gooey::styles::Styles;
|
use gooey::styles::Styles;
|
||||||
use gooey::widget::{Widget, Widgets};
|
use gooey::widget::{Widget, Widgets};
|
||||||
use gooey::widgets::array::Array;
|
use gooey::widgets::stack::Stack;
|
||||||
use gooey::widgets::{Button, Style};
|
use gooey::widgets::{Button, Style};
|
||||||
use gooey::window::Window;
|
use gooey::window::Window;
|
||||||
use gooey::{styles, Run};
|
use gooey::{styles, Run};
|
||||||
|
|
@ -9,7 +9,7 @@ use kludgine::Color;
|
||||||
|
|
||||||
fn main() -> gooey::Result {
|
fn main() -> gooey::Result {
|
||||||
Window::for_widget(
|
Window::for_widget(
|
||||||
Array::rows(
|
Stack::rows(
|
||||||
Widgets::new()
|
Widgets::new()
|
||||||
.with_widget(Button::new("Default"))
|
.with_widget(Button::new("Default"))
|
||||||
.with_widget(red_text(Button::new("Styled"))),
|
.with_widget(red_text(Button::new("Styled"))),
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,8 @@ impl Tick {
|
||||||
|
|
||||||
/// Processes `input`.
|
/// Processes `input`.
|
||||||
///
|
///
|
||||||
/// If the event matches a key that has been marked as
|
/// If the event matches a key that has been marked as handled, [`HANDLED`]
|
||||||
/// handled, [`HANDLED`] will be returned. Otherwise, [`UNHANDLED`] will be
|
/// will be returned. Otherwise, [`IGNORED`] will be returned,
|
||||||
/// returned,
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn key_input(&self, input: &KeyEvent) -> EventHandling {
|
pub fn key_input(&self, input: &KeyEvent) -> EventHandling {
|
||||||
let mut state = self.data.state();
|
let mut state = self.data.state();
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
//! Built-in [`Widget`](crate::widget::Widget) implementations.
|
//! Built-in [`Widget`](crate::widget::Widget) implementations.
|
||||||
|
|
||||||
pub mod array;
|
|
||||||
mod button;
|
mod button;
|
||||||
mod canvas;
|
mod canvas;
|
||||||
mod input;
|
mod input;
|
||||||
mod label;
|
mod label;
|
||||||
|
pub mod stack;
|
||||||
mod style;
|
mod style;
|
||||||
mod tilemap;
|
mod tilemap;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,11 @@ use crate::widget::{ManagedWidget, Widget, Widgets};
|
||||||
use crate::ConstraintLimit;
|
use crate::ConstraintLimit;
|
||||||
|
|
||||||
/// A widget that displays a collection of [`Widgets`] in a
|
/// A widget that displays a collection of [`Widgets`] in a
|
||||||
/// [direction](ArrayDirection).
|
/// [direction](StackDirection).
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Array {
|
pub struct Stack {
|
||||||
/// The direction to display the children using.
|
/// The direction to display the children using.
|
||||||
pub direction: Value<ArrayDirection>,
|
pub direction: Value<StackDirection>,
|
||||||
/// The children widgets that belong to this array.
|
/// The children widgets that belong to this array.
|
||||||
pub children: Value<Widgets>,
|
pub children: Value<Widgets>,
|
||||||
layout: Layout,
|
layout: Layout,
|
||||||
|
|
@ -25,10 +25,10 @@ pub struct Array {
|
||||||
synced_children: Vec<ManagedWidget>,
|
synced_children: Vec<ManagedWidget>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Array {
|
impl Stack {
|
||||||
/// Returns a new widget with the given direction and widgets.
|
/// Returns a new widget with the given direction and widgets.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
direction: impl IntoValue<ArrayDirection>,
|
direction: impl IntoValue<StackDirection>,
|
||||||
widgets: impl IntoValue<Widgets>,
|
widgets: impl IntoValue<Widgets>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut direction = direction.into_value();
|
let mut direction = direction.into_value();
|
||||||
|
|
@ -46,12 +46,12 @@ impl Array {
|
||||||
|
|
||||||
/// Returns a new instance that displays `widgets` in a series of columns.
|
/// Returns a new instance that displays `widgets` in a series of columns.
|
||||||
pub fn columns(widgets: impl IntoValue<Widgets>) -> Self {
|
pub fn columns(widgets: impl IntoValue<Widgets>) -> Self {
|
||||||
Self::new(ArrayDirection::columns(), widgets)
|
Self::new(StackDirection::columns(), widgets)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a new instance that displays `widgets` in a series of rows.
|
/// Returns a new instance that displays `widgets` in a series of rows.
|
||||||
pub fn rows(widgets: impl IntoValue<Widgets>) -> Self {
|
pub fn rows(widgets: impl IntoValue<Widgets>) -> Self {
|
||||||
Self::new(ArrayDirection::rows(), widgets)
|
Self::new(StackDirection::rows(), widgets)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn synchronize_children(&mut self, context: &mut EventContext<'_, '_>) {
|
fn synchronize_children(&mut self, context: &mut EventContext<'_, '_>) {
|
||||||
|
|
@ -83,7 +83,7 @@ impl Array {
|
||||||
// This is a brand new child.
|
// This is a brand new child.
|
||||||
self.synced_children
|
self.synced_children
|
||||||
.insert(index, context.push_child(widget.clone()));
|
.insert(index, context.push_child(widget.clone()));
|
||||||
self.layout.insert(index, ArrayDimension::FitContent);
|
self.layout.insert(index, StackDimension::FitContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -99,7 +99,7 @@ impl Array {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for Array {
|
impl Widget for Stack {
|
||||||
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
|
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
|
||||||
self.synchronize_children(&mut context.as_event_context());
|
self.synchronize_children(&mut context.as_event_context());
|
||||||
self.layout.update(
|
self.layout.update(
|
||||||
|
|
@ -144,31 +144,21 @@ impl Widget for Array {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The direction of an [`Array`] widget.
|
/// The direction of an [`Stack`] widget.
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
pub struct ArrayDirection {
|
pub struct StackDirection {
|
||||||
/// The orientation of the widgets.
|
/// The orientation of the widgets.
|
||||||
pub orientation: ArrayOrientation,
|
pub orientation: StackOrientationn,
|
||||||
/// If true, the widgets will be laid out in reverse order.
|
/// If true, the widgets will be laid out in reverse order.
|
||||||
pub reverse: bool,
|
pub reverse: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The orientation (Row/Column) of an [`Array`] widget.
|
impl StackDirection {
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
|
||||||
|
|
||||||
pub enum ArrayOrientation {
|
|
||||||
/// The child widgets should be displayed as rows.
|
|
||||||
Row,
|
|
||||||
/// The child widgets should be displayed as columns.
|
|
||||||
Column,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ArrayDirection {
|
|
||||||
/// Display child widgets as columns.
|
/// Display child widgets as columns.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn columns() -> Self {
|
pub const fn columns() -> Self {
|
||||||
Self {
|
Self {
|
||||||
orientation: ArrayOrientation::Column,
|
orientation: StackOrientationn::Column,
|
||||||
reverse: false,
|
reverse: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -177,7 +167,7 @@ impl ArrayDirection {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn columns_rev() -> Self {
|
pub const fn columns_rev() -> Self {
|
||||||
Self {
|
Self {
|
||||||
orientation: ArrayOrientation::Column,
|
orientation: StackOrientationn::Column,
|
||||||
reverse: true,
|
reverse: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -186,7 +176,7 @@ impl ArrayDirection {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn rows() -> Self {
|
pub const fn rows() -> Self {
|
||||||
Self {
|
Self {
|
||||||
orientation: ArrayOrientation::Row,
|
orientation: StackOrientationn::Row,
|
||||||
reverse: false,
|
reverse: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -195,7 +185,7 @@ impl ArrayDirection {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn rows_rev() -> Self {
|
pub const fn rows_rev() -> Self {
|
||||||
Self {
|
Self {
|
||||||
orientation: ArrayOrientation::Row,
|
orientation: StackOrientationn::Row,
|
||||||
reverse: true,
|
reverse: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -203,31 +193,41 @@ impl ArrayDirection {
|
||||||
/// Splits a size into its measured and other parts.
|
/// Splits a size into its measured and other parts.
|
||||||
pub(crate) fn split_size<U>(self, s: Size<U>) -> (U, U) {
|
pub(crate) fn split_size<U>(self, s: Size<U>) -> (U, U) {
|
||||||
match self.orientation {
|
match self.orientation {
|
||||||
ArrayOrientation::Row => (s.height, s.width),
|
StackOrientationn::Row => (s.height, s.width),
|
||||||
ArrayOrientation::Column => (s.width, s.height),
|
StackOrientationn::Column => (s.width, s.height),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Combines split values into a [`Size`].
|
/// Combines split values into a [`Size`].
|
||||||
pub(crate) fn make_size<U>(self, measured: U, other: U) -> Size<U> {
|
pub(crate) fn make_size<U>(self, measured: U, other: U) -> Size<U> {
|
||||||
match self.orientation {
|
match self.orientation {
|
||||||
ArrayOrientation::Row => Size::new(other, measured),
|
StackOrientationn::Row => Size::new(other, measured),
|
||||||
ArrayOrientation::Column => Size::new(measured, other),
|
StackOrientationn::Column => Size::new(measured, other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Combines split values into a [`Point`].
|
/// Combines split values into a [`Point`].
|
||||||
pub(crate) fn make_point<U>(self, measured: U, other: U) -> Point<U> {
|
pub(crate) fn make_point<U>(self, measured: U, other: U) -> Point<U> {
|
||||||
match self.orientation {
|
match self.orientation {
|
||||||
ArrayOrientation::Row => Point::new(other, measured),
|
StackOrientationn::Row => Point::new(other, measured),
|
||||||
ArrayOrientation::Column => Point::new(measured, other),
|
StackOrientationn::Column => Point::new(measured, other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The strategy to use when laying a widget out inside of an [`Array`].
|
/// The orientation (Row/Column) of an [`Stack`] widget.
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
pub enum ArrayDimension {
|
|
||||||
|
pub enum StackOrientationn {
|
||||||
|
/// The child widgets should be displayed as rows.
|
||||||
|
Row,
|
||||||
|
/// The child widgets should be displayed as columns.
|
||||||
|
Column,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The strategy to use when laying a widget out inside of an [`Stack`].
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub enum StackDimension {
|
||||||
/// Attempt to lay out the widget based on its contents.
|
/// Attempt to lay out the widget based on its contents.
|
||||||
FitContent,
|
FitContent,
|
||||||
/// Use a fractional amount of the available space.
|
/// Use a fractional amount of the available space.
|
||||||
|
|
@ -242,24 +242,24 @@ pub enum ArrayDimension {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Layout {
|
struct Layout {
|
||||||
children: OrderedLots<ArrayDimension>,
|
children: OrderedLots<StackDimension>,
|
||||||
layouts: Vec<ArrayLayout>,
|
layouts: Vec<StackLayout>,
|
||||||
pub other: UPx,
|
pub other: UPx,
|
||||||
total_weights: u32,
|
total_weights: u32,
|
||||||
allocated_space: UPx,
|
allocated_space: UPx,
|
||||||
fractional: Vec<(LotId, u8)>,
|
fractional: Vec<(LotId, u8)>,
|
||||||
measured: Vec<LotId>,
|
measured: Vec<LotId>,
|
||||||
pub orientation: ArrayDirection,
|
pub orientation: StackDirection,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
struct ArrayLayout {
|
struct StackLayout {
|
||||||
pub offset: UPx,
|
pub offset: UPx,
|
||||||
pub size: UPx,
|
pub size: UPx,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layout {
|
impl Layout {
|
||||||
pub const fn new(orientation: ArrayDirection) -> Self {
|
pub const fn new(orientation: StackDirection) -> Self {
|
||||||
Self {
|
Self {
|
||||||
orientation,
|
orientation,
|
||||||
children: OrderedLots::new(),
|
children: OrderedLots::new(),
|
||||||
|
|
@ -273,23 +273,23 @@ impl Layout {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)] // only used in testing
|
#[cfg(test)] // only used in testing
|
||||||
pub fn push(&mut self, child: ArrayDimension) {
|
pub fn push(&mut self, child: StackDimension) {
|
||||||
self.insert(self.len(), child);
|
self.insert(self.len(), child);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(&mut self, index: usize) -> ArrayDimension {
|
pub fn remove(&mut self, index: usize) -> StackDimension {
|
||||||
let (id, dimension) = self.children.remove_by_index(index).expect("invalid index");
|
let (id, dimension) = self.children.remove_by_index(index).expect("invalid index");
|
||||||
self.layouts.remove(index);
|
self.layouts.remove(index);
|
||||||
|
|
||||||
match dimension {
|
match dimension {
|
||||||
ArrayDimension::FitContent => {
|
StackDimension::FitContent => {
|
||||||
self.measured.retain(|&measured| measured != id);
|
self.measured.retain(|&measured| measured != id);
|
||||||
}
|
}
|
||||||
ArrayDimension::Fractional { weight } => {
|
StackDimension::Fractional { weight } => {
|
||||||
self.fractional.retain(|(measured, _)| *measured != id);
|
self.fractional.retain(|(measured, _)| *measured != id);
|
||||||
self.total_weights -= u32::from(weight);
|
self.total_weights -= u32::from(weight);
|
||||||
}
|
}
|
||||||
ArrayDimension::Exact(size) => {
|
StackDimension::Exact(size) => {
|
||||||
self.allocated_space -= size;
|
self.allocated_space -= size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -307,26 +307,26 @@ impl Layout {
|
||||||
self.children.swap(a, b);
|
self.children.swap(a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, index: usize, child: ArrayDimension) {
|
pub fn insert(&mut self, index: usize, child: StackDimension) {
|
||||||
let id = self.children.insert(index, child);
|
let id = self.children.insert(index, child);
|
||||||
let layout = match child {
|
let layout = match child {
|
||||||
ArrayDimension::FitContent => {
|
StackDimension::FitContent => {
|
||||||
self.measured.push(id);
|
self.measured.push(id);
|
||||||
UPx(0)
|
UPx(0)
|
||||||
}
|
}
|
||||||
ArrayDimension::Fractional { weight } => {
|
StackDimension::Fractional { weight } => {
|
||||||
self.total_weights += u32::from(weight);
|
self.total_weights += u32::from(weight);
|
||||||
self.fractional.push((id, weight));
|
self.fractional.push((id, weight));
|
||||||
UPx(0)
|
UPx(0)
|
||||||
}
|
}
|
||||||
ArrayDimension::Exact(size) => {
|
StackDimension::Exact(size) => {
|
||||||
self.allocated_space += size;
|
self.allocated_space += size;
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.layouts.insert(
|
self.layouts.insert(
|
||||||
index,
|
index,
|
||||||
ArrayLayout {
|
StackLayout {
|
||||||
offset: UPx(0),
|
offset: UPx(0),
|
||||||
size: layout,
|
size: layout,
|
||||||
},
|
},
|
||||||
|
|
@ -407,7 +407,7 @@ impl Layout {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Layout {
|
impl Deref for Layout {
|
||||||
type Target = [ArrayLayout];
|
type Target = [StackLayout];
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.layouts
|
&self.layouts
|
||||||
|
|
@ -421,12 +421,12 @@ mod tests {
|
||||||
use kludgine::figures::units::UPx;
|
use kludgine::figures::units::UPx;
|
||||||
use kludgine::figures::Size;
|
use kludgine::figures::Size;
|
||||||
|
|
||||||
use super::{ArrayDimension, ArrayDirection, Layout};
|
use super::{Layout, StackDimension, StackDirection};
|
||||||
use crate::ConstraintLimit;
|
use crate::ConstraintLimit;
|
||||||
|
|
||||||
struct Child {
|
struct Child {
|
||||||
size: UPx,
|
size: UPx,
|
||||||
dimension: ArrayDimension,
|
dimension: StackDimension,
|
||||||
other: UPx,
|
other: UPx,
|
||||||
divisible_by: Option<UPx>,
|
divisible_by: Option<UPx>,
|
||||||
}
|
}
|
||||||
|
|
@ -435,19 +435,19 @@ mod tests {
|
||||||
pub fn new(size: impl Into<UPx>, other: impl Into<UPx>) -> Self {
|
pub fn new(size: impl Into<UPx>, other: impl Into<UPx>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
size: size.into(),
|
size: size.into(),
|
||||||
dimension: ArrayDimension::FitContent,
|
dimension: StackDimension::FitContent,
|
||||||
other: other.into(),
|
other: other.into(),
|
||||||
divisible_by: None,
|
divisible_by: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fixed_size(mut self, size: UPx) -> Self {
|
pub fn fixed_size(mut self, size: UPx) -> Self {
|
||||||
self.dimension = ArrayDimension::Exact(size);
|
self.dimension = StackDimension::Exact(size);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn weighted(mut self, weight: u8) -> Self {
|
pub fn weighted(mut self, weight: u8) -> Self {
|
||||||
self.dimension = ArrayDimension::Fractional { weight };
|
self.dimension = StackDimension::Fractional { weight };
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -458,7 +458,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_measured_children_in_orientation(
|
fn assert_measured_children_in_orientation(
|
||||||
orientation: ArrayDirection,
|
orientation: StackDirection,
|
||||||
children: &[Child],
|
children: &[Child],
|
||||||
available: Size<ConstraintLimit>,
|
available: Size<ConstraintLimit>,
|
||||||
expected: &[UPx],
|
expected: &[UPx],
|
||||||
|
|
@ -510,18 +510,18 @@ mod tests {
|
||||||
expected_other: UPx,
|
expected_other: UPx,
|
||||||
) {
|
) {
|
||||||
assert_measured_children_in_orientation(
|
assert_measured_children_in_orientation(
|
||||||
ArrayDirection::rows(),
|
StackDirection::rows(),
|
||||||
children,
|
children,
|
||||||
ArrayDirection::rows().make_size(main_constraint, other_constraint),
|
StackDirection::rows().make_size(main_constraint, other_constraint),
|
||||||
expected,
|
expected,
|
||||||
ArrayDirection::rows().make_size(expected_measured, expected_other),
|
StackDirection::rows().make_size(expected_measured, expected_other),
|
||||||
);
|
);
|
||||||
assert_measured_children_in_orientation(
|
assert_measured_children_in_orientation(
|
||||||
ArrayDirection::columns(),
|
StackDirection::columns(),
|
||||||
children,
|
children,
|
||||||
ArrayDirection::columns().make_size(main_constraint, other_constraint),
|
StackDirection::columns().make_size(main_constraint, other_constraint),
|
||||||
expected,
|
expected,
|
||||||
ArrayDirection::columns().make_size(expected_measured, expected_other),
|
StackDirection::columns().make_size(expected_measured, expected_other),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Loading…
Reference in a new issue