use crate::i_headered::{register_headered, IHeadered};
use crate::primitives::HeaderedContentControl;
use crate::templates::IDataTemplate;
use crate::utils::debug_display::{append_optional_boxed_value, build_base_debug_display};
use crate::{AssignedBinding, ContentControl, GridLength, GridUnitType, TableView};
use ferroui_base::layout::HorizontalAlignment;
use ferroui_base::styling::ControlTheme;
use ferroui_base::{
ferro_class, ferro_class_info, ferro_impl_classes, ferro_properties, instantiate, BoxedValue, DirectProperty,
FerroObjectImpl, FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs, Nullable, Ref, StyledElement, StyledElementImpl,
StyledProperty, StyledPropertyMetadata, StyledPropertyOptions, Visual, WeakRef,
};
use std::cell::{Cell, RefCell};
use std::rc::Rc;
#[repr(C)]
pub struct TableViewColumn {
base: StyledElement,
table_view: RefCell<Option<WeakRef<TableView>>>,
actual_width: Cell<f64>,
can_user_effectively_resize: Cell<bool>,
}
ferro_class!(TableViewColumn: StyledElement);
ferro_class_info!(TableViewColumn {
new: TableViewColumn::new,
markup: {
property_attributes: [
CellTemplate: [InheritDataTypeFromItems("ItemsSource", AncestorType = type(Ref<TableView>))],
Binding: [InheritDataTypeFromItems("ItemsSource", AncestorType = type(Ref<TableView>))],
],
},
});
ferro_impl_classes!(TableViewColumn: StyledElementImpl);
impl FerroObjectImpl for TableViewColumn {
fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
Self::parent_on_property_changed(this, change);
let property = change.property();
if property == Self::width_property().as_property() || property == Self::is_visible_property().as_property()
{
if let Some(table_view) = this.table_view() {
table_view.on_columns_size_changed();
}
}
if property == Self::can_user_resize_property().as_property() {
this.update_can_user_effectively_resize();
} else {
if Self::is_header_property(property) {
if let Some(table_view) = this.table_view() {
table_view.refresh_column_headers(&this.to_ref());
}
}
if Self::is_cell_property(property) {
if let Some(table_view) = this.table_view() {
table_view.refresh_column_cells(&this.to_ref());
}
}
}
}
}
ferro_properties! {
impl TableViewColumn {
pub fn is_visible_property() -> StyledProperty<bool> {
Visual::is_visible_property().add_owner::<TableViewColumn>()
}
pub fn header_theme_property() -> StyledProperty<Option<Ref<ControlTheme>>> {
FerroProperty::register::<TableViewColumn, _>("HeaderTheme", None)
}
pub fn header_template_property() -> StyledProperty<Option<Rc<dyn IDataTemplate>>> {
HeaderedContentControl::header_template_property().add_owner::<TableViewColumn>()
}
pub fn header_property() -> StyledProperty<Option<BoxedValue>> {
HeaderedContentControl::header_property().add_owner::<TableViewColumn>()
}
pub fn width_property() -> StyledProperty<GridLength> {
FerroProperty::register::<TableViewColumn, _>("Width", GridLength::new(1.0, GridUnitType::Star))
}
pub fn cell_theme_property() -> StyledProperty<Option<Ref<ControlTheme>>> {
FerroProperty::register::<TableViewColumn, _>("CellTheme", None)
}
pub fn cell_template_property() -> StyledProperty<Option<Rc<dyn IDataTemplate>>> {
FerroProperty::register::<TableViewColumn, _>("CellTemplate", None)
}
pub fn binding_property() -> StyledProperty<Option<AssignedBinding>> {
FerroProperty::register_with::<TableViewColumn, _>(
"Binding",
StyledPropertyOptions::new(None).assign_binding(true),
)
}
pub fn can_user_resize_property() -> StyledProperty<Option<bool>> {
FerroProperty::register::<TableViewColumn, _>("CanUserResize", None)
}
pub fn horizontal_content_alignment_property() -> StyledProperty<HorizontalAlignment> {
ContentControl::horizontal_content_alignment_property()
.add_owner_with::<TableViewColumn>(StyledPropertyMetadata::new(Some(HorizontalAlignment::Left)))
}
pub fn table_view_property() -> DirectProperty<TableViewColumn, Option<Ref<TableView>>> {
FerroProperty::register_direct::<TableViewColumn, _>("TableView", |o| o.table_view(), None, None)
}
pub fn actual_width_property() -> DirectProperty<TableViewColumn, f64> {
FerroProperty::register_direct::<TableViewColumn, _>("ActualWidth", |o| o.actual_width(), None, 0.0)
}
pub fn can_user_effectively_resize_property() -> DirectProperty<TableViewColumn, bool> {
FerroProperty::register_direct::<TableViewColumn, _>(
"CanUserEffectivelyResize",
|o| o.can_user_effectively_resize(),
None,
false,
)
}
}
}
impl TableViewColumn {
fn static_constructor() {
register_headered::<TableViewColumn>();
}
pub fn construct() -> Self {
Self {
base: StyledElement::construct(),
table_view: RefCell::new(None),
actual_width: Cell::new(f64::NAN),
can_user_effectively_resize: Cell::new(false),
}
}
pub fn new() -> Ref<Self> {
instantiate(Self::construct())
}
pub fn is_visible(&self) -> bool {
self.get_value(Self::is_visible_property())
}
pub fn set_is_visible(&self, value: bool) {
self.set_value(Self::is_visible_property(), value)
}
pub fn header_theme(&self) -> Option<Ref<ControlTheme>> {
self.get_value(Self::header_theme_property())
}
pub fn set_header_theme(&self, value: impl Into<Nullable<ControlTheme>>) {
self.set_value(Self::header_theme_property(), value.into().0)
}
pub fn header_template(&self) -> Option<Rc<dyn IDataTemplate>> {
self.get_value(Self::header_template_property())
}
pub fn set_header_template(&self, value: Option<Rc<dyn IDataTemplate>>) {
self.set_value(Self::header_template_property(), value)
}
pub fn header(&self) -> Option<BoxedValue> {
self.get_value(Self::header_property())
}
pub fn set_header(&self, value: Option<BoxedValue>) {
self.set_value(Self::header_property(), value)
}
pub fn width(&self) -> GridLength {
self.get_value(Self::width_property())
}
pub fn set_width(&self, value: GridLength) {
self.set_value(Self::width_property(), value)
}
pub fn cell_theme(&self) -> Option<Ref<ControlTheme>> {
self.get_value(Self::cell_theme_property())
}
pub fn set_cell_theme(&self, value: impl Into<Nullable<ControlTheme>>) {
self.set_value(Self::cell_theme_property(), value.into().0)
}
pub fn cell_template(&self) -> Option<Rc<dyn IDataTemplate>> {
self.get_value(Self::cell_template_property())
}
pub fn set_cell_template(&self, value: Option<Rc<dyn IDataTemplate>>) {
self.set_value(Self::cell_template_property(), value)
}
pub fn binding(&self) -> Option<AssignedBinding> {
self.get_value(Self::binding_property())
}
pub fn set_binding(&self, value: Option<AssignedBinding>) {
self.set_value(Self::binding_property(), value)
}
pub fn can_user_resize(&self) -> Option<bool> {
self.get_value(Self::can_user_resize_property())
}
pub fn set_can_user_resize(&self, value: Option<bool>) {
self.set_value(Self::can_user_resize_property(), value)
}
pub fn horizontal_content_alignment(&self) -> HorizontalAlignment {
self.get_value(Self::horizontal_content_alignment_property())
}
pub fn set_horizontal_content_alignment(&self, value: HorizontalAlignment) {
self.set_value(Self::horizontal_content_alignment_property(), value)
}
pub fn table_view(&self) -> Option<Ref<TableView>> {
self.table_view.borrow().as_ref().and_then(WeakRef::upgrade)
}
pub(crate) fn set_table_view(&self, value: Option<Ref<TableView>>) {
let old_value = self.table_view();
if old_value == value {
return;
}
*self.table_view.borrow_mut() = value.as_ref().map(Ref::downgrade);
self.raise_direct_property_changed(Self::table_view_property(), &old_value, &value);
self.update_can_user_effectively_resize();
}
pub fn actual_width(&self) -> f64 {
self.actual_width.get()
}
pub(crate) fn set_actual_width(&self, value: f64) {
if value.is_nan() && self.actual_width.get().is_nan() {
return;
}
self.set_and_raise_cell(Self::actual_width_property(), &self.actual_width, value);
}
pub fn can_user_effectively_resize(&self) -> bool {
self.can_user_effectively_resize.get()
}
pub(crate) fn set_can_user_effectively_resize(&self, value: bool) {
self.set_and_raise_cell(
Self::can_user_effectively_resize_property(),
&self.can_user_effectively_resize,
value,
);
}
fn is_cell_property(property: &'static FerroProperty) -> bool {
property == Self::cell_theme_property().as_property()
|| property == Self::cell_template_property().as_property()
|| property == Self::binding_property().as_property()
|| property == Self::horizontal_content_alignment_property().as_property()
|| property == Self::is_visible_property().as_property()
}
fn is_header_property(property: &'static FerroProperty) -> bool {
property == Self::header_theme_property().as_property()
|| property == Self::header_template_property().as_property()
|| property == Self::header_property().as_property()
|| property == Self::horizontal_content_alignment_property().as_property()
|| property == Self::is_visible_property().as_property()
}
pub(crate) fn update_can_user_effectively_resize(&self) {
let value = self
.can_user_resize()
.or_else(|| self.table_view().map(|table_view| table_view.can_user_resize_columns()))
.unwrap_or(true);
self.set_can_user_effectively_resize(value);
}
pub(crate) fn debug_display(&self) -> String {
let mut builder = String::new();
self.build_debug_display(&mut builder, true);
builder
}
pub(crate) fn build_debug_display(&self, builder: &mut String, include_content: bool) {
build_base_debug_display(self, builder);
if include_content {
append_optional_boxed_value(builder, "Header", self.header().as_ref(), include_content);
}
}
}
impl IHeadered for TableViewColumn {
fn header(&self) -> Option<BoxedValue> {
TableViewColumn::header(self)
}
fn set_header(&self, value: Option<BoxedValue>) {
TableViewColumn::set_header(self, value)
}
}