From aa7148b3b80c0040a3ed3ce2b0475792e2bc3069 Mon Sep 17 00:00:00 2001 From: TheEthicalBoy <98978078+ndonkoHenri@users.noreply.github.com> Date: Sat, 6 Jul 2024 01:52:30 +0200 Subject: [PATCH] Fix issues with `*Button`s (#3582) * BorderSide: add stroke_align prop * proper styling in buttons * deprecate ThemeVisualDensity in favor of VisualDensity * ButtonStyle: new props * deprecation: disabled_color is renamed to disabled_bgcolor --- .../lib/src/controls/cupertino_button.dart | 37 +++++++---- packages/flet/lib/src/utils/borders.dart | 1 + packages/flet/lib/src/utils/buttons.dart | 64 +++++++++++-------- .../flet-core/src/flet_core/__init__.py | 5 +- .../flet-core/src/flet_core/border.py | 28 +++++--- .../flet-core/src/flet_core/buttons.py | 39 +++++++++-- .../flet-core/src/flet_core/checkbox.py | 11 ++-- .../packages/flet-core/src/flet_core/chip.py | 23 +++---- .../flet-core/src/flet_core/control.py | 2 +- .../src/flet_core/cupertino_button.py | 31 ++++++++- .../src/flet_core/cupertino_filled_button.py | 18 +++--- .../src/flet_core/elevated_button.py | 46 ++++++------- .../src/flet_core/embed_json_encoder.py | 1 + .../flet-core/src/flet_core/expansion_tile.py | 11 ++-- .../src/flet_core/floating_action_button.py | 3 + .../flet-core/src/flet_core/icon_button.py | 11 ++-- .../flet-core/src/flet_core/list_tile.py | 11 ++-- .../src/flet_core/outlined_button.py | 3 + .../packages/flet-core/src/flet_core/radio.py | 11 ++-- .../packages/flet-core/src/flet_core/theme.py | 31 ++------- .../packages/flet-core/src/flet_core/types.py | 50 ++++++++++++--- 21 files changed, 274 insertions(+), 163 deletions(-) diff --git a/packages/flet/lib/src/controls/cupertino_button.dart b/packages/flet/lib/src/controls/cupertino_button.dart index 6496ff062..3e561d9ec 100644 --- a/packages/flet/lib/src/controls/cupertino_button.dart +++ b/packages/flet/lib/src/controls/cupertino_button.dart @@ -38,6 +38,7 @@ class _CupertinoButtonControlState extends State { Widget build(BuildContext context) { debugPrint("CupertinoButton build: ${widget.control.id}"); bool disabled = widget.control.isDisabled || widget.parentDisabled; + var theme = Theme.of(context); var contentCtrls = widget.children.where((c) => c.name == "content" && c.isVisible); @@ -59,7 +60,11 @@ class _CupertinoButtonControlState extends State { if (icon != null) { children.add(Icon( selected ? selectedIcon : icon, - color: selected ? selectedIconColor : iconColor, + color: selected + ? selectedIconColor + : disabled + ? theme.disabledColor + : iconColor, size: iconSize, )); } @@ -92,8 +97,10 @@ class _CupertinoButtonControlState extends State { double pressedOpacity = widget.control.attrDouble("opacityOnClick", 0.4)!; double minSize = widget.control.attrDouble("minSize", 44.0)!; String url = widget.control.attrString("url", "")!; - Color disabledColor = widget.control.attrColor("disabledColor", context) ?? - CupertinoColors.quaternarySystemFill; + Color disabledColor = + widget.control.attrColor("disabledBgcolor", context) ?? + widget.control.attrColor("disabledColor", context) ?? // deprecated + CupertinoColors.quaternarySystemFill; Color? bgColor = widget.control.attrColor("bgColor", context); Color? color = widget.control.attrColor("color", context); AlignmentGeometry alignment = @@ -103,7 +110,6 @@ class _CupertinoButtonControlState extends State { EdgeInsets? padding = parseEdgeInsets(widget.control, "padding"); - var theme = Theme.of(context); var style = parseButtonStyle(Theme.of(context), widget.control, "style", defaultForegroundColor: theme.colorScheme.primary, defaultBackgroundColor: Colors.transparent, @@ -117,18 +123,21 @@ class _CupertinoButtonControlState extends State { ? const StadiumBorder() : RoundedRectangleBorder(borderRadius: BorderRadius.circular(4))); - if (padding == null && style != null) { - padding = style.padding?.resolve({}) as EdgeInsets?; - } + if (style != null) { + Set widgetStates = selected ? {WidgetState.selected} : {}; - if (bgColor == null && style != null) { - bgColor = style.backgroundColor - ?.resolve(selected ? {WidgetState.selected} : {}); - } + // Check if the widget is disabled and update the foregroundColor accordingly + // backgroundColor is not updated here, as it is handled by disabledColor + if (disabled) { + style = style.copyWith( + foregroundColor: WidgetStatePropertyAll(theme.disabledColor), + ); + } - if (color == null && style != null) { - color = style.foregroundColor - ?.resolve(selected ? {WidgetState.selected} : {}); + // Resolve color, background color, and padding based on widget states + color = style.foregroundColor?.resolve(widgetStates); + bgColor = style.backgroundColor?.resolve(widgetStates); + padding = style.padding?.resolve({}) as EdgeInsets?; } if (color != null) { diff --git a/packages/flet/lib/src/utils/borders.dart b/packages/flet/lib/src/utils/borders.dart index 1bc5192cc..a57c15119 100644 --- a/packages/flet/lib/src/utils/borders.dart +++ b/packages/flet/lib/src/utils/borders.dart @@ -90,6 +90,7 @@ BorderSide? borderSideFromJSON(ThemeData? theme, dynamic json, color: parseColor(theme, json['c'], defaultSideColor ?? Colors.black)!, width: parseDouble(json['w'], 1)!, + strokeAlign: parseDouble(json['sa'], BorderSide.strokeAlignInside)!, style: BorderStyle.solid) : null; } diff --git a/packages/flet/lib/src/utils/buttons.dart b/packages/flet/lib/src/utils/buttons.dart index 09eab05b1..f35f27722 100644 --- a/packages/flet/lib/src/utils/buttons.dart +++ b/packages/flet/lib/src/utils/buttons.dart @@ -4,11 +4,15 @@ import 'package:flet/src/utils/transforms.dart'; import 'package:flutter/material.dart'; import '../models/control.dart'; +import 'alignment.dart'; import 'borders.dart'; import 'colors.dart'; import 'edge_insets.dart'; import 'material_state.dart'; +import 'mouse.dart'; import 'numbers.dart'; +import 'text.dart'; +import 'theme.dart'; ButtonStyle? parseButtonStyle(ThemeData theme, Control control, String propName, {required Color defaultForegroundColor, @@ -54,31 +58,41 @@ ButtonStyle? buttonStyleFromJSON(ThemeData theme, Map? json, return null; } return ButtonStyle( - foregroundColor: getWidgetStateProperty(json["color"], - (jv) => parseColor(theme, jv as String), defaultForegroundColor), - backgroundColor: getWidgetStateProperty(json["bgcolor"], - (jv) => parseColor(theme, jv as String), defaultBackgroundColor), - overlayColor: getWidgetStateProperty(json["overlay_color"], - (jv) => parseColor(theme, jv as String), defaultOverlayColor), - shadowColor: getWidgetStateProperty(json["shadow_color"], - (jv) => parseColor(theme, jv as String), defaultShadowColor), - surfaceTintColor: getWidgetStateProperty( - json["surface_tint_color"], - (jv) => parseColor(theme, jv as String), - defaultSurfaceTintColor), - elevation: getWidgetStateProperty( - json["elevation"], (jv) => parseDouble(jv, 0)!, defaultElevation), - animationDuration: json["animation_duration"] != null - ? Duration(milliseconds: parseInt(json["animation_duration"], 0)!) - : null, - padding: getWidgetStateProperty( - json["padding"], (jv) => edgeInsetsFromJson(jv), defaultPadding), - side: getWidgetStateProperty( - json["side"], - (jv) => borderSideFromJSON(theme, jv, theme.colorScheme.outline), - defaultBorderSide), - shape: getWidgetStateProperty( - json["shape"], (jv) => outlinedBorderFromJSON(jv), defaultShape)); + foregroundColor: getWidgetStateProperty(json["color"], + (jv) => parseColor(theme, jv as String), defaultForegroundColor), + backgroundColor: getWidgetStateProperty(json["bgcolor"], + (jv) => parseColor(theme, jv as String), defaultBackgroundColor), + overlayColor: getWidgetStateProperty(json["overlay_color"], + (jv) => parseColor(theme, jv as String), defaultOverlayColor), + shadowColor: getWidgetStateProperty(json["shadow_color"], + (jv) => parseColor(theme, jv as String), defaultShadowColor), + surfaceTintColor: getWidgetStateProperty(json["surface_tint_color"], + (jv) => parseColor(theme, jv as String), defaultSurfaceTintColor), + elevation: getWidgetStateProperty( + json["elevation"], (jv) => parseDouble(jv, 0)!, defaultElevation), + animationDuration: json["animation_duration"] != null + ? Duration(milliseconds: parseInt(json["animation_duration"], 0)!) + : null, + padding: getWidgetStateProperty( + json["padding"], (jv) => edgeInsetsFromJson(jv), defaultPadding), + side: getWidgetStateProperty( + json["side"], + (jv) => borderSideFromJSON(theme, jv, theme.colorScheme.outline), + defaultBorderSide), + shape: getWidgetStateProperty( + json["shape"], (jv) => outlinedBorderFromJSON(jv), defaultShape), + iconColor: getWidgetStateProperty(json["icon_color"], + (jv) => parseColor(theme, jv as String), defaultForegroundColor), + alignment: alignmentFromJson(json["alignment"]), + enableFeedback: parseBool(json["enable_feedback"]), + textStyle: getWidgetStateProperty( + json["text_style"], (jv) => textStyleFromJson(theme, jv)), + iconSize: getWidgetStateProperty( + json["icon_size"], (jv) => parseDouble(jv)), + visualDensity: parseVisualDensity(json["visual_density"]), + mouseCursor: getWidgetStateProperty( + json["mouse_cursor"], (jv) => parseMouseCursor(jv)), + ); } FloatingActionButtonLocation parseFloatingActionButtonLocation( diff --git a/sdk/python/packages/flet-core/src/flet_core/__init__.py b/sdk/python/packages/flet-core/src/flet_core/__init__.py index 4b649fd6f..c3c35845b 100644 --- a/sdk/python/packages/flet-core/src/flet_core/__init__.py +++ b/sdk/python/packages/flet-core/src/flet_core/__init__.py @@ -36,7 +36,7 @@ from flet_core.badge import Badge from flet_core.banner import Banner from flet_core.blur import Blur, BlurTileMode -from flet_core.border import Border, BorderSide +from flet_core.border import Border, BorderSide, BorderSideStrokeAlign from flet_core.border_radius import BorderRadius from flet_core.bottom_app_bar import BottomAppBar from flet_core.bottom_sheet import BottomSheet @@ -321,7 +321,6 @@ TabsTheme, TextTheme, Theme, - ThemeVisualDensity, TimePickerTheme, TooltipTheme, ) @@ -361,6 +360,8 @@ TabAlignment, TextAlign, ThemeMode, + ThemeVisualDensity, + VisualDensity, UrlTarget, VerticalAlignment, StrokeCap, diff --git a/sdk/python/packages/flet-core/src/flet_core/border.py b/sdk/python/packages/flet-core/src/flet_core/border.py index 76801d8c2..a12639e8c 100644 --- a/sdk/python/packages/flet-core/src/flet_core/border.py +++ b/sdk/python/packages/flet-core/src/flet_core/border.py @@ -1,19 +1,29 @@ -import dataclasses +from dataclasses import dataclass, field +from enum import Enum from typing import Optional, Union +from flet_core.types import OptionalNumber -@dataclasses.dataclass + +class BorderSideStrokeAlign(Enum): + STROKE_ALIGN_INSIDE = -1.0 + STROKE_ALIGN_CENTER = 0.0 + STROKE_ALIGN_OUTSIDE = 1.0 + + +@dataclass class BorderSide: - width: Union[None, float, int] - color: Optional[str] = dataclasses.field(default=None) + width: OptionalNumber + color: Optional[str] = field(default=None) + stroke_align: Union[BorderSideStrokeAlign, OptionalNumber] = field(default=None) -@dataclasses.dataclass +@dataclass class Border: - top: Optional[BorderSide] = dataclasses.field(default=None) - right: Optional[BorderSide] = dataclasses.field(default=None) - bottom: Optional[BorderSide] = dataclasses.field(default=None) - left: Optional[BorderSide] = dataclasses.field(default=None) + top: Optional[BorderSide] = field(default=None) + right: Optional[BorderSide] = field(default=None) + bottom: Optional[BorderSide] = field(default=None) + left: Optional[BorderSide] = field(default=None) def all(width: Optional[float] = None, color: Optional[str] = None) -> Border: diff --git a/sdk/python/packages/flet-core/src/flet_core/buttons.py b/sdk/python/packages/flet-core/src/flet_core/buttons.py index b13c2e7b8..9e50fc944 100644 --- a/sdk/python/packages/flet-core/src/flet_core/buttons.py +++ b/sdk/python/packages/flet-core/src/flet_core/buttons.py @@ -1,8 +1,18 @@ from dataclasses import dataclass, field from typing import Dict, Optional, Union +from flet_core.alignment import Alignment from flet_core.border import BorderSide -from flet_core.types import BorderRadiusValue, ControlState, PaddingValue +from flet_core.text_style import TextStyle +from flet_core.types import ( + BorderRadiusValue, + ControlState, + PaddingValue, + Number, + ThemeVisualDensity, + VisualDensity, + MouseCursor, +) @dataclass @@ -48,9 +58,7 @@ def __post_init__(self): @dataclass class ButtonStyle: color: Union[None, str, Dict[Union[str, ControlState], str]] = field(default=None) - bgcolor: Union[None, str, Dict[Union[str, ControlState], str]] = field( - default=None - ) + bgcolor: Union[None, str, Dict[Union[str, ControlState], str]] = field(default=None) overlay_color: Union[None, str, Dict[Union[str, ControlState], str]] = field( default=None ) @@ -73,3 +81,26 @@ class ButtonStyle: shape: Union[ None, OutlinedBorder, Dict[Union[str, ControlState], OutlinedBorder] ] = field(default=None) + alignment: Union[ + None, Alignment, Dict[Union[str, ControlState], Alignment] + ] = field(default=None) + enable_feedback: Union[None, bool, Dict[Union[str, ControlState], bool]] = field( + default=None + ) + text_style: Union[ + None, TextStyle, Dict[Union[str, ControlState], TextStyle] + ] = field(default=None) + icon_size: Union[None, Number, Dict[Union[str, ControlState], Number]] = field( + default=None + ) + icon_color: Union[None, str, Dict[Union[str, ControlState], str]] = field( + default=None + ) + visual_density: Union[ + None, + Union[VisualDensity, ThemeVisualDensity], + Dict[Union[str, ControlState], Union[VisualDensity, ThemeVisualDensity]], + ] = field(default=None) + mouse_cursor: Union[ + None, MouseCursor, Dict[Union[str, ControlState], MouseCursor] + ] = field(default=None) diff --git a/sdk/python/packages/flet-core/src/flet_core/checkbox.py b/sdk/python/packages/flet-core/src/flet_core/checkbox.py index ac570b1eb..68d956d9f 100644 --- a/sdk/python/packages/flet-core/src/flet_core/checkbox.py +++ b/sdk/python/packages/flet-core/src/flet_core/checkbox.py @@ -7,7 +7,6 @@ from flet_core.control import OptionalNumber from flet_core.ref import Ref from flet_core.text_style import TextStyle -from flet_core.theme import ThemeVisualDensity from flet_core.types import ( AnimationValue, LabelPosition, @@ -18,6 +17,8 @@ RotateValue, ScaleValue, OptionalEventCallable, + ThemeVisualDensity, + VisualDensity, ) @@ -74,7 +75,7 @@ def __init__( splash_radius: OptionalNumber = None, border_side: Union[None, BorderSide, Dict[ControlState, BorderSide]] = None, is_error: Optional[bool] = None, - visual_density: Optional[ThemeVisualDensity] = None, + visual_density: Union[None, ThemeVisualDensity, VisualDensity] = None, mouse_cursor: Optional[MouseCursor] = None, on_change: OptionalEventCallable = None, on_focus: OptionalEventCallable = None, @@ -230,13 +231,13 @@ def mouse_cursor(self, value: Optional[MouseCursor]): # visual_density @property - def visual_density(self) -> Optional[ThemeVisualDensity]: + def visual_density(self) -> Union[None, ThemeVisualDensity, VisualDensity]: return self.__visual_density @visual_density.setter - def visual_density(self, value: Optional[ThemeVisualDensity]): + def visual_density(self, value: Union[None, ThemeVisualDensity, VisualDensity]): self.__visual_density = value - self._set_enum_attr("visualDensity", value, ThemeVisualDensity) + self._set_enum_attr("visualDensity", value, ThemeVisualDensity, VisualDensity) # autofocus @property diff --git a/sdk/python/packages/flet-core/src/flet_core/chip.py b/sdk/python/packages/flet-core/src/flet_core/chip.py index 6c23ee104..e5cd2de63 100644 --- a/sdk/python/packages/flet-core/src/flet_core/chip.py +++ b/sdk/python/packages/flet-core/src/flet_core/chip.py @@ -6,7 +6,6 @@ from flet_core.control import Control, OptionalNumber from flet_core.ref import Ref from flet_core.text_style import TextStyle -from flet_core.theme import ThemeVisualDensity from flet_core.types import ( AnimationValue, OffsetValue, @@ -17,6 +16,8 @@ ClipBehavior, ControlState, OptionalEventCallable, + ThemeVisualDensity, + VisualDensity, ) @@ -91,13 +92,13 @@ def __init__( color: Union[None, str, Dict[Union[ControlState, str], str]] = None, click_elevation: OptionalNumber = None, clip_behavior: Optional[ClipBehavior] = None, - visual_density: Optional[ThemeVisualDensity] = None, + visual_density: Union[None, ThemeVisualDensity, VisualDensity] = None, border_side: Optional[BorderSide] = None, - on_click: OptionalEventCallable = None, - on_delete: OptionalEventCallable = None, - on_select: OptionalEventCallable = None, - on_focus: OptionalEventCallable = None, - on_blur: OptionalEventCallable = None, + on_click: OptionalEventCallable = None, + on_delete: OptionalEventCallable = None, + on_select: OptionalEventCallable = None, + on_focus: OptionalEventCallable = None, + on_blur: OptionalEventCallable = None, # # ConstrainedControl # @@ -122,7 +123,7 @@ def __init__( animate_rotation: AnimationValue = None, animate_scale: AnimationValue = None, animate_offset: AnimationValue = None, - on_animation_end: OptionalEventCallable = None, + on_animation_end: OptionalEventCallable = None, tooltip: Optional[str] = None, visible: Optional[bool] = None, disabled: Optional[bool] = None, @@ -416,13 +417,13 @@ def shape(self, value: Optional[OutlinedBorder]): # visual_density @property - def visual_density(self) -> Optional[ThemeVisualDensity]: + def visual_density(self) -> Union[None, ThemeVisualDensity, VisualDensity]: return self.__visual_density @visual_density.setter - def visual_density(self, value: Optional[ThemeVisualDensity]): + def visual_density(self, value: Union[None, ThemeVisualDensity, VisualDensity]): self.__visual_density = value - self._set_enum_attr("visualDensity", value, ThemeVisualDensity) + self._set_enum_attr("visualDensity", value, ThemeVisualDensity, VisualDensity) # clip_behavior @property diff --git a/sdk/python/packages/flet-core/src/flet_core/control.py b/sdk/python/packages/flet-core/src/flet_core/control.py index 465ffcf25..5ea3dfc8e 100644 --- a/sdk/python/packages/flet-core/src/flet_core/control.py +++ b/sdk/python/packages/flet-core/src/flet_core/control.py @@ -141,7 +141,7 @@ def _set_attr(self, name: str, value: V, dirty: bool = True) -> None: self._set_attr_internal(name, value, dirty) def _set_enum_attr( - self, name: str, value: V, enum_type: Type[Enum], dirty: bool = True + self, name: str, value: V, *enum_type: Type[Enum], dirty: bool = True ) -> None: self._set_attr_internal( name, value.value if isinstance(value, enum_type) else value, dirty diff --git a/sdk/python/packages/flet-core/src/flet_core/cupertino_button.py b/sdk/python/packages/flet-core/src/flet_core/cupertino_button.py index d86827b76..b536e9d34 100644 --- a/sdk/python/packages/flet-core/src/flet_core/cupertino_button.py +++ b/sdk/python/packages/flet-core/src/flet_core/cupertino_button.py @@ -1,3 +1,4 @@ +import warnings from typing import Any, Optional, Union from flet_core.alignment import Alignment @@ -35,6 +36,7 @@ def __init__( bgcolor: Optional[str] = None, color: Optional[str] = None, disabled_color: Optional[str] = None, + disabled_bgcolor: Optional[str] = None, opacity_on_click: OptionalNumber = None, min_size: OptionalNumber = None, padding: PaddingValue = None, @@ -44,7 +46,7 @@ def __init__( url_target: Optional[UrlTarget] = None, on_click: OptionalEventCallable = None, # - # Common + # ConstrainedControl # ref: Optional[Ref] = None, key: Optional[str] = None, @@ -106,6 +108,7 @@ def __init__( ) self.disabled_color = disabled_color + self.disabled_bgcolor = disabled_bgcolor self.text = text self.icon = icon self.icon_color = icon_color @@ -127,8 +130,8 @@ def _get_control_name(self): def before_update(self): super().before_update() assert ( - self.text or self.__content - ), "at minimum, text or content must be provided" + self.text or self.icon or (self.__content and self.__content.visible) + ), "at minimum, text, icon or a visible content must be provided" self._set_attr_json("padding", self.__padding) self._set_attr_json("borderRadius", self.__border_radius) self._set_attr_json("alignment", self.__alignment) @@ -178,11 +181,33 @@ def alignment(self, value: Optional[Alignment]): # disabled_color @property def disabled_color(self) -> Optional[str]: + warnings.warn( + f"disabled_color is deprecated since version 0.24.0 " + f"and will be removed in version 0.27.0. Use disabled_bgcolor instead.", + category=DeprecationWarning, + stacklevel=2, + ) return self._get_attr("disabledColor") @disabled_color.setter def disabled_color(self, value: Optional[str]): self._set_attr("disabledColor", value) + if value is not None: + warnings.warn( + f"disabled_color is deprecated since version 0.24.0 " + f"and will be removed in version 0.27.0. Use disabled_bgcolor instead.", + category=DeprecationWarning, + stacklevel=2, + ) + + # disabled_bgcolor + @property + def disabled_bgcolor(self) -> Optional[str]: + return self._get_attr("disabledBgcolor") + + @disabled_bgcolor.setter + def disabled_bgcolor(self, value: Optional[str]): + self._set_attr("disabledBgcolor", value) # opacity_on_click @property diff --git a/sdk/python/packages/flet-core/src/flet_core/cupertino_filled_button.py b/sdk/python/packages/flet-core/src/flet_core/cupertino_filled_button.py index fad1e98d4..7f9cee1f6 100644 --- a/sdk/python/packages/flet-core/src/flet_core/cupertino_filled_button.py +++ b/sdk/python/packages/flet-core/src/flet_core/cupertino_filled_button.py @@ -31,12 +31,21 @@ def main(page: ft.Page): def __init__( self, text: Optional[str] = None, + icon: Optional[str] = None, + icon_color: Optional[str] = None, + content: Optional[Control] = None, disabled_color: Optional[str] = None, opacity_on_click: OptionalNumber = None, min_size: OptionalNumber = None, padding: PaddingValue = None, alignment: Optional[Alignment] = None, border_radius: BorderRadiusValue = None, + url: Optional[str] = None, + url_target: Optional[UrlTarget] = None, + on_click=None, + # + # ConstrainedControl + # ref: Optional[Ref] = None, key: Optional[str] = None, width: OptionalNumber = None, @@ -49,15 +58,6 @@ def __init__( visible: Optional[bool] = None, disabled: Optional[bool] = None, data: Any = None, - # - # Specific - # - icon: Optional[str] = None, - icon_color: Optional[str] = None, - content: Optional[Control] = None, - url: Optional[str] = None, - url_target: Optional[UrlTarget] = None, - on_click=None, ): CupertinoButton.__init__( self, diff --git a/sdk/python/packages/flet-core/src/flet_core/elevated_button.py b/sdk/python/packages/flet-core/src/flet_core/elevated_button.py index 64caefc98..d8127e69a 100644 --- a/sdk/python/packages/flet-core/src/flet_core/elevated_button.py +++ b/sdk/python/packages/flet-core/src/flet_core/elevated_button.py @@ -153,35 +153,29 @@ def _get_control_name(self): def before_update(self): super().before_update() - if ( - self.__color is not None - or self.__bgcolor is not None - or self.__elevation is not None - ): - if self.__style is None: - self.__style = ButtonStyle() - if self.__style.color != self.__color or self.disabled: - # if the color is set through the style, use it - if not ( - self.__class__.__name__ in ["FilledButton", "FilledTonalButton"] - and self.__style.color - and not self.disabled - ): - self.__style.color = self.__color if not self.disabled else None - if self.__style.bgcolor != self.__bgcolor or self.disabled: - # if the bgcolor is set through the style, use it - if not ( - self.__class__.__name__ in ["FilledButton", "FilledTonalButton"] - and self.__style.bgcolor - and not self.disabled - ): - self.__style.bgcolor = self.__bgcolor if not self.disabled else None - if self.__style.elevation != self.__elevation: - self.__style.elevation = self.__elevation - if self.__style is not None: + assert ( + self.text or self.icon or (self.__content and self.__content.visible) + ), "at minimum, text, icon or a visible content must be provided" + if any([self.__color, self.__bgcolor, self.__elevation]): + self.__style = self.__style or ButtonStyle() + if self.__style: + self.__style.color = ( + self.__style.color if self.__style.color is not None else self.color + ) + self.__style.bgcolor = ( + self.__style.bgcolor + if self.__style.bgcolor is not None + else self.bgcolor + ) + self.__style.elevation = ( + self.__style.elevation + if self.__style.elevation is not None + else self.elevation + ) self.__style.side = self._wrap_attr_dict(self.__style.side) self.__style.shape = self._wrap_attr_dict(self.__style.shape) self.__style.padding = self._wrap_attr_dict(self.__style.padding) + self.__style.text_style = self._wrap_attr_dict(self.__style.text_style) self._set_attr_json("style", self.__style) def _get_children(self): diff --git a/sdk/python/packages/flet-core/src/flet_core/embed_json_encoder.py b/sdk/python/packages/flet-core/src/flet_core/embed_json_encoder.py index eae26184b..8002baaa2 100644 --- a/sdk/python/packages/flet-core/src/flet_core/embed_json_encoder.py +++ b/sdk/python/packages/flet-core/src/flet_core/embed_json_encoder.py @@ -14,6 +14,7 @@ def default(self, obj): return { "w": obj.width, "c": obj.color, + "sa": obj.stroke_align, } elif isinstance(obj, Border): return { diff --git a/sdk/python/packages/flet-core/src/flet_core/expansion_tile.py b/sdk/python/packages/flet-core/src/flet_core/expansion_tile.py index 207b3c03e..3b226538d 100644 --- a/sdk/python/packages/flet-core/src/flet_core/expansion_tile.py +++ b/sdk/python/packages/flet-core/src/flet_core/expansion_tile.py @@ -7,7 +7,6 @@ from flet_core.constrained_control import ConstrainedControl from flet_core.control import Control, OptionalNumber from flet_core.ref import Ref -from flet_core.theme import ThemeVisualDensity from flet_core.types import ( AnimationValue, ClipBehavior, @@ -18,6 +17,8 @@ RotateValue, ScaleValue, OptionalEventCallable, + ThemeVisualDensity, + VisualDensity, ) @@ -61,7 +62,7 @@ def __init__( collapsed_shape: Optional[OutlinedBorder] = None, dense: Optional[bool] = None, enable_feedback: Optional[bool] = None, - visual_density: Optional[ThemeVisualDensity] = None, + visual_density: Union[None, ThemeVisualDensity, VisualDensity] = None, on_change: OptionalEventCallable = None, # # ConstrainedControl @@ -308,13 +309,13 @@ def clip_behavior(self, value: Optional[ClipBehavior]): # visual_density @property - def visual_density(self) -> Optional[ThemeVisualDensity]: + def visual_density(self) -> Union[None, ThemeVisualDensity, VisualDensity]: return self.__visual_density @visual_density.setter - def visual_density(self, value: Optional[ThemeVisualDensity]): + def visual_density(self, value: Union[None, ThemeVisualDensity, VisualDensity]): self.__visual_density = value - self._set_enum_attr("visualDensity", value, ThemeVisualDensity) + self._set_enum_attr("visualDensity", value, ThemeVisualDensity, VisualDensity) # maintain_state @property diff --git a/sdk/python/packages/flet-core/src/flet_core/floating_action_button.py b/sdk/python/packages/flet-core/src/flet_core/floating_action_button.py index 9302e22ff..2fd2b5fdb 100644 --- a/sdk/python/packages/flet-core/src/flet_core/floating_action_button.py +++ b/sdk/python/packages/flet-core/src/flet_core/floating_action_button.py @@ -176,6 +176,9 @@ def _get_control_name(self): def before_update(self): super().before_update() + assert ( + self.text or self.icon or (self.__content and self.__content.visible) + ), "at minimum, text, icon or a visible content must be provided" self._set_attr_json("shape", self.__shape) def _get_children(self): diff --git a/sdk/python/packages/flet-core/src/flet_core/icon_button.py b/sdk/python/packages/flet-core/src/flet_core/icon_button.py index 7c3a8e526..de1c458d7 100644 --- a/sdk/python/packages/flet-core/src/flet_core/icon_button.py +++ b/sdk/python/packages/flet-core/src/flet_core/icon_button.py @@ -7,7 +7,6 @@ from flet_core.constrained_control import ConstrainedControl from flet_core.control import Control, OptionalNumber from flet_core.ref import Ref -from flet_core.theme import ThemeVisualDensity from flet_core.types import ( AnimationValue, MouseCursor, @@ -18,6 +17,8 @@ ScaleValue, UrlTarget, OptionalEventCallable, + ThemeVisualDensity, + VisualDensity, ) from flet_core.utils import deprecated @@ -85,7 +86,7 @@ def __init__( url: Optional[str] = None, url_target: Optional[UrlTarget] = None, mouse_cursor: Optional[MouseCursor] = None, - visual_density: Optional[ThemeVisualDensity] = None, + visual_density: Union[None, ThemeVisualDensity, VisualDensity] = None, on_click: OptionalEventCallable = None, on_focus: OptionalEventCallable = None, on_blur: OptionalEventCallable = None, @@ -386,13 +387,13 @@ def mouse_cursor(self, value: Optional[MouseCursor]): # visual_density @property - def visual_density(self) -> Optional[ThemeVisualDensity]: + def visual_density(self) -> Union[None, ThemeVisualDensity, VisualDensity]: return self.__visual_density @visual_density.setter - def visual_density(self, value: Optional[ThemeVisualDensity]): + def visual_density(self, value: Union[None, ThemeVisualDensity, VisualDensity]): self.__visual_density = value - self._set_enum_attr("visualDensity", value, ThemeVisualDensity) + self._set_enum_attr("visualDensity", value, ThemeVisualDensity, VisualDensity) # on_click @property diff --git a/sdk/python/packages/flet-core/src/flet_core/list_tile.py b/sdk/python/packages/flet-core/src/flet_core/list_tile.py index ed4ce3d39..142fb30f5 100644 --- a/sdk/python/packages/flet-core/src/flet_core/list_tile.py +++ b/sdk/python/packages/flet-core/src/flet_core/list_tile.py @@ -7,7 +7,6 @@ from flet_core.control import Control, OptionalNumber from flet_core.ref import Ref from flet_core.text_style import TextStyle -from flet_core.theme import ThemeVisualDensity from flet_core.types import ( AnimationValue, MouseCursor, @@ -18,6 +17,8 @@ ScaleValue, UrlTarget, OptionalEventCallable, + ThemeVisualDensity, + VisualDensity, ) @@ -103,7 +104,7 @@ def __init__( icon_color: Optional[str] = None, text_color: Optional[str] = None, shape: Optional[OutlinedBorder] = None, - visual_density: Optional[ThemeVisualDensity] = None, + visual_density: Union[None, ThemeVisualDensity, VisualDensity] = None, mouse_cursor: Optional[MouseCursor] = None, title_text_style: Optional[TextStyle] = None, subtitle_text_style: Optional[TextStyle] = None, @@ -493,13 +494,13 @@ def mouse_cursor(self, value: Optional[MouseCursor]): # visual_density @property - def visual_density(self) -> Optional[ThemeVisualDensity]: + def visual_density(self) -> Union[None, ThemeVisualDensity, VisualDensity]: return self.__visual_density @visual_density.setter - def visual_density(self, value: Optional[ThemeVisualDensity]): + def visual_density(self, value: Union[None, ThemeVisualDensity, VisualDensity]): self.__visual_density = value - self._set_enum_attr("visualDensity", value, ThemeVisualDensity) + self._set_enum_attr("visualDensity", value, ThemeVisualDensity, VisualDensity) # shape @property diff --git a/sdk/python/packages/flet-core/src/flet_core/outlined_button.py b/sdk/python/packages/flet-core/src/flet_core/outlined_button.py index 04184d647..9141d0a43 100644 --- a/sdk/python/packages/flet-core/src/flet_core/outlined_button.py +++ b/sdk/python/packages/flet-core/src/flet_core/outlined_button.py @@ -142,6 +142,9 @@ def _get_control_name(self): def before_update(self): super().before_update() + assert ( + self.text or self.icon or (self.__content and self.__content.visible) + ), "at minimum, text, icon or a visible content must be provided" if self.__style is not None: self.__style.side = self._wrap_attr_dict(self.__style.side) self.__style.shape = self._wrap_attr_dict(self.__style.shape) diff --git a/sdk/python/packages/flet-core/src/flet_core/radio.py b/sdk/python/packages/flet-core/src/flet_core/radio.py index 2e6246bca..d509df42e 100644 --- a/sdk/python/packages/flet-core/src/flet_core/radio.py +++ b/sdk/python/packages/flet-core/src/flet_core/radio.py @@ -5,7 +5,6 @@ from flet_core.control import OptionalNumber from flet_core.ref import Ref from flet_core.text_style import TextStyle -from flet_core.theme import ThemeVisualDensity from flet_core.types import ( AnimationValue, LabelPosition, @@ -16,6 +15,8 @@ RotateValue, ScaleValue, OptionalEventCallable, + ThemeVisualDensity, + VisualDensity, ) try: @@ -68,7 +69,7 @@ def __init__( focus_color: Optional[str] = None, splash_radius: OptionalNumber = None, toggleable: Optional[bool] = None, - visual_density: Optional[ThemeVisualDensity] = None, + visual_density: Union[None, ThemeVisualDensity, VisualDensity] = None, mouse_cursor: Optional[MouseCursor] = None, on_focus: OptionalEventCallable = None, on_blur: OptionalEventCallable = None, @@ -211,13 +212,13 @@ def toggleable(self, value: Optional[bool]): # visual_density @property - def visual_density(self) -> Optional[ThemeVisualDensity]: + def visual_density(self) -> Union[None, ThemeVisualDensity, VisualDensity]: return self.__visual_density @visual_density.setter - def visual_density(self, value: Optional[ThemeVisualDensity]): + def visual_density(self, value: Union[None, ThemeVisualDensity, VisualDensity]): self.__visual_density = value - self._set_enum_attr("visualDensity", value, ThemeVisualDensity) + self._set_enum_attr("visualDensity", value, ThemeVisualDensity, VisualDensity) # label @property diff --git a/sdk/python/packages/flet-core/src/flet_core/theme.py b/sdk/python/packages/flet-core/src/flet_core/theme.py index 84eed5f29..74e71197a 100644 --- a/sdk/python/packages/flet-core/src/flet_core/theme.py +++ b/sdk/python/packages/flet-core/src/flet_core/theme.py @@ -1,7 +1,6 @@ from dataclasses import dataclass, field -from enum import Enum, EnumMeta +from enum import Enum from typing import Dict, List, Optional, Union -from warnings import warn from flet_core.alignment import Alignment from flet_core.border import BorderSide @@ -24,6 +23,8 @@ MouseCursor, OffsetValue, PaddingValue, + ThemeVisualDensity, + VisualDensity, ) try: @@ -32,26 +33,6 @@ from typing_extensions import Literal -class ThemeVisualDensityDeprecated(EnumMeta): - def __getattribute__(self, item): - if item == "ADAPTIVEPLATFORMDENSITY": - warn( - "ADAPTIVEPLATFORMDENSITY is deprecated and will be removed in version 0.26.0. " - "Use ADAPTIVE_PLATFORM_DENSITY instead.", - DeprecationWarning, - stacklevel=2, - ) - return EnumMeta.__getattribute__(self, item) - - -class ThemeVisualDensity(Enum, metaclass=ThemeVisualDensityDeprecated): - STANDARD = "standard" - COMPACT = "compact" - COMFORTABLE = "comfortable" - ADAPTIVEPLATFORMDENSITY = "adaptivePlatformDensity" - ADAPTIVE_PLATFORM_DENSITY = "adaptivePlatformDensity" - - PageTransitionString = Literal["fadeUpwards", "openUpwards", "zoom", "cupertino"] @@ -305,7 +286,7 @@ class RadioTheme: ) splash_radius: OptionalNumber = field(default=None) height: OptionalNumber = field(default=None) - visual_density: Optional[ThemeVisualDensity] = field(default=None) + visual_density: Union[None, ThemeVisualDensity, VisualDensity] = field(default=None) mouse_cursor: Union[ None, MouseCursor, Dict[Union[str, ControlState], MouseCursor] ] = field(default=None) @@ -324,7 +305,7 @@ class CheckboxTheme: ) splash_radius: OptionalNumber = field(default=None) border_side: Optional[BorderSide] = field(default=None) - visual_density: Optional[ThemeVisualDensity] = field(default=None) + visual_density: Union[None, ThemeVisualDensity, VisualDensity] = field(default=None) shape: Optional[OutlinedBorder] = field(default=None) mouse_cursor: Union[ None, MouseCursor, Dict[Union[str, ControlState], MouseCursor] @@ -509,7 +490,7 @@ class ListTileTheme: enable_feedback: Optional[bool] = field(default=None) dense: Optional[bool] = field(default=None) shape: Optional[OutlinedBorder] = field(default=None) - visual_density: Optional[ThemeVisualDensity] = field(default=None) + visual_density: Union[None, ThemeVisualDensity, VisualDensity] = field(default=None) content_padding: PaddingValue = field(default=None) min_vertical_padding: PaddingValue = field(default=None) horizontal_spacing: OptionalNumber = field(default=None) diff --git a/sdk/python/packages/flet-core/src/flet_core/types.py b/sdk/python/packages/flet-core/src/flet_core/types.py index 0801c6b34..a8aab744b 100644 --- a/sdk/python/packages/flet-core/src/flet_core/types.py +++ b/sdk/python/packages/flet-core/src/flet_core/types.py @@ -101,15 +101,15 @@ class NotchShape(Enum): class MaterialStateDeprecated(EnumMeta): def __getattribute__(self, item): if item in [ - "hovered", - "focused", - "pressed", - "dragged", - "selected", - "scrolledUnder", - "disabled", - "error", - "", + "HOVERED", + "FOCUSED", + "PRESSED", + "DRAGGED", + "SELECTED", + "SCROLLED_UNDER", + "DISABLED", + "ERROR", + "DEFAULT", ]: warn( "MaterialState enum is deprecated and will be removed in version 0.26.0. " @@ -360,6 +360,38 @@ class StrokeJoin(Enum): BEVEL = "bevel" +class ThemeVisualDensityDeprecated(EnumMeta): + def __getattribute__(self, item): + if item in [ + "STANDARD", + "COMPACT", + "COMFORTABLE", + "ADAPTIVE_PLATFORM_DENSITY", + ]: + warn( + "ThemeVisualDensity enum is deprecated and will be removed in version 0.27.0. " + "Use VisualDensity enum instead.", + DeprecationWarning, + stacklevel=2, + ) + return EnumMeta.__getattribute__(self, item) + + +class ThemeVisualDensity(Enum, metaclass=ThemeVisualDensityDeprecated): + STANDARD = "standard" + COMPACT = "compact" + COMFORTABLE = "comfortable" + ADAPTIVEPLATFORMDENSITY = "adaptivePlatformDensity" + ADAPTIVE_PLATFORM_DENSITY = "adaptivePlatformDensity" + + +class VisualDensity(Enum): + STANDARD = "standard" + COMPACT = "compact" + COMFORTABLE = "comfortable" + ADAPTIVE_PLATFORM_DENSITY = "adaptivePlatformDensity" + + # Events OptionalEventCallable = Optional[Callable[[ControlEvent], None]]