-
-
Notifications
You must be signed in to change notification settings - Fork 653
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
1 parent
134a3ff
commit fd6d44b
Showing
3 changed files
with
111 additions
and
49 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
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,67 +1,101 @@ | ||
from travertino.size import at_least | ||
|
||
from ..container import TogaContainer | ||
from ..libs import Gtk | ||
from .base import Widget | ||
|
||
|
||
class TogaSplitContainer(Gtk.Paned): | ||
def __init__(self, impl): | ||
Gtk.Paned.__init__(self) | ||
self._impl = impl | ||
self.interface = self._impl.interface | ||
class SplitContainer(Widget): | ||
def create(self): | ||
self.native = Gtk.Paned() | ||
self.native.set_wide_handle(True) | ||
|
||
def do_size_allocate(self, allocation): | ||
Gtk.Paned.do_size_allocate(self, allocation) | ||
self.sub_containers = [TogaContainer(), TogaContainer()] | ||
self.native.pack1(self.sub_containers[0], True, False) | ||
self.native.pack2(self.sub_containers[1], True, False) | ||
|
||
# Turn all the weights into a fraction of 1.0 | ||
total = sum(self.interface._weight) | ||
self.interface._weight = [weight / total for weight in self.interface._weight] | ||
self._split_proportion = 0.5 | ||
|
||
# Set the position of splitter depending on the weight of splits. | ||
self.set_position( | ||
int( | ||
self.interface._weight[0] * self.get_allocated_width() | ||
if self.interface.direction == self.interface.VERTICAL | ||
else self.get_allocated_height() | ||
) | ||
) | ||
def set_bounds(self, x, y, width, height): | ||
super().set_bounds(x, y, width, height) | ||
|
||
# If we've got a pending split to apply, set the split position. | ||
# However, only do this if the layout is more than the min size; | ||
# there are initial 0-sized layouts for which the split is meaningless. | ||
if ( | ||
self._split_proportion | ||
and width >= self.interface._MIN_WIDTH | ||
and height > self.interface._MIN_HEIGHT | ||
): | ||
if self.interface.direction == self.interface.VERTICAL: | ||
position = int(self._split_proportion * width) | ||
else: | ||
position = int(self._split_proportion * height) | ||
|
||
class SplitContainer(Widget): | ||
def create(self): | ||
self.native = TogaSplitContainer(self) | ||
self.native.set_wide_handle(True) | ||
self.native.set_position(position) | ||
self._split_proportion = None | ||
|
||
def set_content(self, content, flex): | ||
# Clear any existing content | ||
for container in self.sub_containers: | ||
container.content = None | ||
|
||
# Add all children to the content widget. | ||
for position, widget in enumerate(content): | ||
self.sub_containers[position].content = widget | ||
|
||
# Use Paned widget rather than VPaned and HPaned deprecated widgets | ||
# Note that orientation in toga behave unlike Gtk | ||
if self.interface.direction == self.interface.VERTICAL: | ||
# We now know the initial positions of the split. However, we can't *set* the | ||
# because GTK requires a pixel position, and the widget isn't visible yet. So - | ||
# store the split; and when we do our first layout, apply that proportion. | ||
self._split_proportion = flex[0] / sum(flex) | ||
|
||
def get_direction(self): | ||
if self.native.get_orientation() == Gtk.Orientation.HORIZONTAL: | ||
return self.interface.VERTICAL | ||
else: | ||
return self.interface.HORIZONTAL | ||
|
||
def set_direction(self, value): | ||
if value == self.interface.VERTICAL: | ||
self.native.set_orientation(Gtk.Orientation.HORIZONTAL) | ||
elif self.interface.direction == self.interface.HORIZONTAL: | ||
self.native.set_orientation(Gtk.Orientation.VERTICAL) | ||
else: | ||
raise ValueError("Allowed orientation is VERTICAL or HORIZONTAL") | ||
self.native.set_orientation(Gtk.Orientation.VERTICAL) | ||
|
||
def add_content(self, position, widget, flex): | ||
# Add all children to the content widget. | ||
sub_container = TogaContainer() | ||
sub_container.content = widget | ||
def rehint(self): | ||
# This is a SWAG (scientific wild-ass guess). There doesn't appear to be | ||
# an actual API to get the true size of the splitter. 10px seems enough. | ||
SPLITTER_WIDTH = 10 | ||
if self.interface.direction == self.interface.HORIZONTAL: | ||
# When the splitter is horizontal, the splitcontainer must be | ||
# at least as wide as it's widest sub-container, and at least | ||
# as tall as the minimum height of all subcontainers, plus the | ||
# height of the splitter itself. Enforce a minimum size in both | ||
# axies | ||
min_width = self.interface._MIN_WIDTH | ||
min_height = 0 | ||
for sub_container in self.sub_containers: | ||
# Make sure the subcontainer's size is up to date | ||
sub_container.recompute() | ||
|
||
if position >= 2: | ||
raise ValueError("SplitContainer content must be a 2-tuple") | ||
min_width = max(min_width, sub_container.min_width) | ||
min_height += sub_container.min_height | ||
|
||
if position == 0: | ||
self.native.pack1(sub_container, flex, False) | ||
elif position == 1: | ||
self.native.pack2(sub_container, flex, False) | ||
min_height = max(min_height, self.interface._MIN_HEIGHT) + SPLITTER_WIDTH | ||
else: | ||
# When the splitter is vertical, the splitcontainer must be | ||
# at least as tall as it's tallest sub-container, and at least | ||
# as wide as the minimum width of all subcontainers, plus the | ||
# width of the splitter itself. | ||
min_width = 0 | ||
min_height = self.interface._MIN_HEIGHT | ||
for sub_container in self.sub_containers: | ||
# Make sure the subcontainer's size is up to date | ||
sub_container.recompute() | ||
|
||
def set_app(self, app): | ||
if self.interface.content: | ||
self.interface.content[0].app = self.interface.app | ||
self.interface.content[1].app = self.interface.app | ||
min_width += sub_container.min_width | ||
min_height = max(min_height, sub_container.min_height) | ||
|
||
def set_window(self, window): | ||
if self.interface.content: | ||
self.interface.content[0].window = self.interface.window | ||
self.interface.content[1].window = self.interface.window | ||
min_width = max(min_width, self.interface._MIN_WIDTH) + SPLITTER_WIDTH | ||
|
||
def set_direction(self, value): | ||
self.interface.factory.not_implemented("SplitContainer.set_direction()") | ||
self.interface.intrinsic.width = at_least(min_width) | ||
self.interface.intrinsic.height = at_least(min_height) |
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,28 @@ | ||
import asyncio | ||
|
||
from toga_gtk.libs import Gtk | ||
|
||
from .base import SimpleProbe | ||
|
||
|
||
class SplitContainerProbe(SimpleProbe): | ||
native_class = Gtk.Paned | ||
|
||
def move_split(self, position): | ||
self.native.set_position(position) | ||
|
||
def repaint_needed(self): | ||
return ( | ||
self.impl.sub_containers[0].needs_redraw | ||
or self.impl.sub_containers[1].needs_redraw | ||
or super().repaint_needed() | ||
) | ||
|
||
async def wait_for_split(self): | ||
sub1 = self.impl.sub_containers[0] | ||
position = sub1.get_allocated_height(), sub1.get_allocated_width() | ||
current = None | ||
while position != current: | ||
position = current | ||
await asyncio.sleep(0.05) | ||
current = sub1.get_allocated_height(), sub1.get_allocated_width() |