-
-
Notifications
You must be signed in to change notification settings - Fork 673
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
446 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
release = False | ||
__version__ = "2.0.1.dev0" | ||
__hash__ = "f7bde69707ac708a758a02d89f14997ee468d1ee" | ||
__short_hash__ = "f7bde69" | ||
__date__ = "2024-02-27" | ||
__hash__ = "072e6fd15a32c2ce8c9cdaa2a9f59c202b841b9e" | ||
__short_hash__ = "072e6fd" | ||
__date__ = "2024-03-17" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .carousel import MDCarousel, MDCarouselImageItem |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import math | ||
|
||
# Made in reference with | ||
# ~/material-components-android/lib/java/com/google/android/material/carousel/Arrangement.java | ||
|
||
|
||
class Arrangement: | ||
MEDIUM_ITEM_FLEX_PERCENTAGE = 0.1 | ||
|
||
def __init__( | ||
self, | ||
priority, | ||
target_small_size, | ||
min_small_size, | ||
max_small_size, | ||
small_count, | ||
target_medium_size, | ||
medium_count, | ||
target_large_size, | ||
large_count, | ||
available_space, | ||
): | ||
self.priority = priority | ||
self.small_size = max(min(target_small_size, max_small_size), min_small_size) | ||
self.small_count = small_count | ||
self.medium_size = target_medium_size | ||
self.medium_count = medium_count | ||
self.large_size = target_large_size | ||
self.large_count = large_count | ||
self.fit(available_space, min_small_size, max_small_size, target_large_size) | ||
self.cost = self.calculate_cost(target_large_size) | ||
|
||
def __str__(self): | ||
return ( | ||
f"Arrangement [priority={self.priority}, small_count={self.small_count}," | ||
f" small_size={self.small_size}, medium_count={self.medium_count}," | ||
f" medium_size={self.medium_size}, large_count={self.large_count}," | ||
f" large_size={self.large_size}, cost={self.cost}]" | ||
) | ||
|
||
def get_space(self): | ||
return ( | ||
(self.large_size * self.large_count) | ||
+ (self.medium_size * self.medium_count) | ||
+ (self.small_size * self.small_count) | ||
) | ||
|
||
def fit(self, available_space, min_small_size, max_small_size, target_large_size): | ||
delta = available_space - self.get_space() | ||
if self.small_count > 0 and delta > 0: | ||
self.small_size += min( | ||
delta / self.small_count, max_small_size - self.small_size | ||
) | ||
elif self.small_count > 0 and delta < 0: | ||
self.small_size += max( | ||
delta / self.small_count, min_small_size - self.small_size | ||
) | ||
self.small_size = self.small_size if self.small_count > 0 else 0 | ||
self.large_size = self.calculate_large_size( | ||
available_space, min_small_size, max_small_size, target_large_size | ||
) | ||
self.medium_size = (self.large_size + self.small_size) / 2 | ||
if self.medium_count > 0 and self.large_size != target_large_size: | ||
target_adjustment = (target_large_size - self.large_size) * self.large_count | ||
available_medium_flex = ( | ||
self.medium_size * self.MEDIUM_ITEM_FLEX_PERCENTAGE | ||
) * self.medium_count | ||
distribute = min(abs(target_adjustment), available_medium_flex) | ||
if target_adjustment > 0: | ||
self.medium_size -= distribute / self.medium_count | ||
self.large_size += distribute / self.large_count | ||
else: | ||
self.medium_size += distribute / self.medium_count | ||
self.large_size -= distribute / self.large_count | ||
|
||
def calculate_large_size( | ||
self, available_space, min_small_size, max_small_size, target_large_size | ||
): | ||
small_size = self.small_size if self.small_count > 0 else 0 | ||
return ( | ||
available_space | ||
- ( | ||
((float(self.small_count)) + (float(self.medium_count)) / 2) | ||
* small_size | ||
) | ||
) / ((float(self.large_count)) + (float(self.medium_count)) / 2) | ||
|
||
def is_valid(self): | ||
if self.large_count > 0 and self.small_count > 0 and self.medium_count > 0: | ||
return ( | ||
self.large_size > self.medium_size | ||
and self.medium_size > self.small_size | ||
) | ||
elif self.large_count > 0 and self.small_count > 0: | ||
return self.large_size > self.small_size | ||
return True | ||
|
||
def calculate_cost(self, target_large_size): | ||
if not self.is_valid(): | ||
return float("inf") | ||
return abs(target_large_size - self.large_size) * self.priority | ||
|
||
@staticmethod | ||
def find_lowest_cost_arrangement( | ||
available_space, | ||
target_small_size, | ||
min_small_size, | ||
max_small_size, | ||
small_counts, | ||
target_medium_size, | ||
medium_counts, | ||
target_large_size, | ||
large_counts, | ||
): | ||
lowest_cost_arrangement = None | ||
priority = 1 | ||
|
||
for large_count in large_counts: | ||
for medium_count in medium_counts: | ||
for small_count in small_counts: | ||
arrangement = Arrangement( | ||
priority, | ||
target_small_size, | ||
min_small_size, | ||
max_small_size, | ||
small_count, | ||
target_medium_size, | ||
medium_count, | ||
target_large_size, | ||
large_count, | ||
available_space, | ||
) | ||
|
||
if ( | ||
lowest_cost_arrangement is None | ||
or arrangement.cost < lowest_cost_arrangement.cost | ||
): | ||
lowest_cost_arrangement = arrangement | ||
if lowest_cost_arrangement.cost == 0: | ||
return lowest_cost_arrangement | ||
priority += 1 | ||
return lowest_cost_arrangement | ||
|
||
def get_item_count(self): | ||
return self.small_count + self.medium_count + self.large_count |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<MDCarouselImageItem>: | ||
text:"" | ||
canvas: | ||
Color: | ||
rgba:[1,1,1,0.5] | ||
RoundedRectangle: | ||
size:self.size | ||
pos:self.pos | ||
radius:[dp(25)] * 4 | ||
MDLabel: | ||
text:root.text | ||
halign:"center" | ||
|
||
<MDCarousel>: | ||
MDBoxLayout: | ||
id:_container | ||
is_horizontal:root.is_horizontal | ||
alignment:root.alignment | ||
spacing:dp(8) | ||
padding:[0, dp(8)] | ||
size_hint:1,1 | ||
adaptive_width:True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import os | ||
from functools import partial | ||
|
||
from kivy.clock import Clock | ||
from kivy.lang import Builder | ||
from kivy.metrics import dp | ||
from kivy.properties import ( | ||
BooleanProperty, | ||
StringProperty, | ||
OptionProperty, | ||
NumericProperty, | ||
ListProperty, | ||
DictProperty, | ||
) | ||
from kivy.factory import Factory | ||
from kivy.uix.image import AsyncImage | ||
from kivy.uix.boxlayout import BoxLayout | ||
from kivy.uix.stencilview import StencilView | ||
|
||
from kivymd import uix_path | ||
from kivymd.uix.boxlayout import MDBoxLayout | ||
from kivymd.uix.carousel.carousel_strategy import AvaliableStrategies | ||
|
||
with open( | ||
os.path.join(uix_path, "carousel", "carousel.kv"), | ||
encoding="utf-8", | ||
) as kv_file: | ||
Builder.load_string(kv_file.read()) | ||
|
||
|
||
class MDCarouselImageItem(BoxLayout): | ||
pass | ||
|
||
|
||
class MDCarousel(MDBoxLayout, StencilView): | ||
strategy = OptionProperty( | ||
"MultiBrowseCarouselStrategy", options=[AvaliableStrategies.avaliable] | ||
) | ||
is_horizontal = BooleanProperty(True) | ||
alignment = StringProperty("default") | ||
desired_item_size = NumericProperty(100) | ||
|
||
data = ListProperty([]) | ||
viewclass = StringProperty("MDCarouselImageItem") | ||
|
||
_strategy = None | ||
_variable_item_size = dp(50) | ||
|
||
def __init__(self, *arg, **kwargs): | ||
super().__init__(*arg, **kwargs) | ||
self.bind(data=self.update_strategy) | ||
self.bind(strategy=self.update_strategy) | ||
self.bind(size=self.update_strategy) | ||
|
||
def on_data(self, instance, data): | ||
for widget_data in data: | ||
widget = Factory.get( | ||
self.viewclass | ||
if "viewclass" not in widget_data.keys() | ||
else widget_data["viewclass"] | ||
)(size_hint_x=None) | ||
for key, value in widget_data.items(): | ||
setattr(widget, key, value) | ||
widget.width = 0 | ||
self.ids._container.add_widget(widget) | ||
|
||
def update_strategy(self, *args): | ||
if self.width <= 0: | ||
Clock.schedule_once(self.update_strategy) | ||
return | ||
if self._strategy.__class__.__name__ != self.strategy: | ||
self._strategy = AvaliableStrategies.get(self.strategy, len(self.data)) | ||
self._strategy.arrange(self.alignment, self.width, self.desired_item_size) | ||
Clock.schedule_once(partial(self._strategy.set_init_size, self.ids._container)) | ||
return self._strategy | ||
|
||
def on_touch_move(self, touch): | ||
self._strategy.touch_move(self.ids._container, touch) | ||
super().on_touch_move(touch) |
Oops, something went wrong.