Added a custom layout for the prefs so that it adjusts to different screen sizes automatically. I may have gotten slightly carried away on this one........
This commit is contained in:
174
flatcamGUI/ColumnarFlowLayout.py
Normal file
174
flatcamGUI/ColumnarFlowLayout.py
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QPoint, QRect, QSize, Qt
|
||||||
|
from PyQt5.QtWidgets import QLayout, QSizePolicy
|
||||||
|
import math
|
||||||
|
|
||||||
|
class ColumnarFlowLayout(QLayout):
|
||||||
|
def __init__(self, parent=None, margin=0, spacing=-1):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
if parent is not None:
|
||||||
|
self.setContentsMargins(margin, margin, margin, margin)
|
||||||
|
|
||||||
|
self.setSpacing(spacing)
|
||||||
|
self.itemList = []
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
item = self.takeAt(0)
|
||||||
|
while item:
|
||||||
|
item = self.takeAt(0)
|
||||||
|
|
||||||
|
def addItem(self, item):
|
||||||
|
self.itemList.append(item)
|
||||||
|
|
||||||
|
def count(self):
|
||||||
|
return len(self.itemList)
|
||||||
|
|
||||||
|
def itemAt(self, index):
|
||||||
|
if 0 <= index < len(self.itemList):
|
||||||
|
return self.itemList[index]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def takeAt(self, index):
|
||||||
|
if 0 <= index < len(self.itemList):
|
||||||
|
return self.itemList.pop(index)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def expandingDirections(self):
|
||||||
|
return Qt.Orientations(Qt.Orientation(0))
|
||||||
|
|
||||||
|
def hasHeightForWidth(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def heightForWidth(self, width):
|
||||||
|
height = self.doLayout(QRect(0, 0, width, 0), True)
|
||||||
|
return height
|
||||||
|
|
||||||
|
def setGeometry(self, rect):
|
||||||
|
super().setGeometry(rect)
|
||||||
|
self.doLayout(rect, False)
|
||||||
|
|
||||||
|
def sizeHint(self):
|
||||||
|
return self.minimumSize()
|
||||||
|
|
||||||
|
def minimumSize(self):
|
||||||
|
size = QSize()
|
||||||
|
|
||||||
|
for item in self.itemList:
|
||||||
|
size = size.expandedTo(item.minimumSize())
|
||||||
|
|
||||||
|
margin, _, _, _ = self.getContentsMargins()
|
||||||
|
|
||||||
|
size += QSize(2 * margin, 2 * margin)
|
||||||
|
return size
|
||||||
|
|
||||||
|
def doLayout(self, rect: QRect, testOnly: bool) -> int:
|
||||||
|
spacing = self.spacing()
|
||||||
|
x = rect.x()
|
||||||
|
y = rect.y()
|
||||||
|
|
||||||
|
# Determine width of widest item
|
||||||
|
widest = 0
|
||||||
|
for item in self.itemList:
|
||||||
|
widest = max(widest, item.sizeHint().width())
|
||||||
|
|
||||||
|
# Determine how many equal-width columns we can get, and how wide each one should be
|
||||||
|
column_count = math.floor(rect.width() / (widest + spacing))
|
||||||
|
column_count = min(column_count, len(self.itemList))
|
||||||
|
column_count = max(1, column_count)
|
||||||
|
column_width = math.floor((rect.width() - (column_count-1)*spacing - 1) / column_count)
|
||||||
|
|
||||||
|
# Get the heights for all of our items
|
||||||
|
item_heights = {}
|
||||||
|
for item in self.itemList:
|
||||||
|
height = item.heightForWidth(column_width) if item.hasHeightForWidth() else item.sizeHint().height()
|
||||||
|
item_heights[item] = height
|
||||||
|
|
||||||
|
# Prepare our column representation
|
||||||
|
column_contents = []
|
||||||
|
column_heights = []
|
||||||
|
for column_index in range(column_count):
|
||||||
|
column_contents.append([])
|
||||||
|
column_heights.append(0)
|
||||||
|
|
||||||
|
def add_to_column(column: int, item):
|
||||||
|
column_contents[column].append(item)
|
||||||
|
column_heights[column] += (item_heights[item] + spacing)
|
||||||
|
|
||||||
|
def shove_one(from_column: int) -> bool:
|
||||||
|
if len(column_contents[from_column]) >= 1:
|
||||||
|
item = column_contents[from_column].pop(0)
|
||||||
|
column_heights[from_column] -= (item_heights[item] + spacing)
|
||||||
|
add_to_column(from_column-1, item)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def shove_cascade_consider(from_column: int) -> bool:
|
||||||
|
changed = False
|
||||||
|
|
||||||
|
if len(column_contents[from_column]) > 1:
|
||||||
|
item = column_contents[from_column][0]
|
||||||
|
item_height = item_heights[item]
|
||||||
|
if column_heights[from_column-1] + item_height < max(column_heights):
|
||||||
|
changed = shove_one(from_column) or changed
|
||||||
|
|
||||||
|
if from_column+1 < column_count:
|
||||||
|
changed = shove_cascade_consider(from_column+1) or changed
|
||||||
|
|
||||||
|
return changed
|
||||||
|
|
||||||
|
def shove_cascade() -> bool:
|
||||||
|
if column_count < 2:
|
||||||
|
return False
|
||||||
|
changed = True
|
||||||
|
while changed:
|
||||||
|
changed = shove_cascade_consider(1)
|
||||||
|
return changed
|
||||||
|
|
||||||
|
def pick_best_shoving_position() -> int:
|
||||||
|
best_pos = 1
|
||||||
|
best_height = sys.maxsize
|
||||||
|
for column_index in range(1, column_count):
|
||||||
|
if len(column_contents[column_index]) == 0:
|
||||||
|
continue
|
||||||
|
item = column_contents[column_index][0]
|
||||||
|
height_after_shove = column_heights[column_index-1] + item_heights[item]
|
||||||
|
if height_after_shove < best_height:
|
||||||
|
best_height = height_after_shove
|
||||||
|
best_pos = column_index
|
||||||
|
return best_pos
|
||||||
|
|
||||||
|
# Calculate the best layout
|
||||||
|
column_index = 0
|
||||||
|
for item in self.itemList:
|
||||||
|
item_height = item_heights[item]
|
||||||
|
if column_heights[column_index] != 0 and (column_heights[column_index] + item_height) > max(column_heights):
|
||||||
|
column_index += 1
|
||||||
|
if column_index >= column_count:
|
||||||
|
# Run out of room, need to shove more stuff in each column
|
||||||
|
if column_count >= 2:
|
||||||
|
changed = shove_cascade()
|
||||||
|
if not changed:
|
||||||
|
shoving_pos = pick_best_shoving_position()
|
||||||
|
shove_one(shoving_pos)
|
||||||
|
shove_cascade()
|
||||||
|
column_index = column_count-1
|
||||||
|
|
||||||
|
add_to_column(column_index, item)
|
||||||
|
|
||||||
|
shove_cascade()
|
||||||
|
|
||||||
|
# Set geometry according to the layout we have calculated
|
||||||
|
if not testOnly:
|
||||||
|
for column_index, items in enumerate(column_contents):
|
||||||
|
x = column_index * (column_width + spacing)
|
||||||
|
y = 0
|
||||||
|
for item in items:
|
||||||
|
height = item_heights[item]
|
||||||
|
item.setGeometry(QRect(x, y, column_width, height))
|
||||||
|
y += (height + spacing)
|
||||||
|
|
||||||
|
# Return the overall height
|
||||||
|
return max(column_heights)
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
from PyQt5 import QtWidgets, QtCore
|
||||||
|
|
||||||
from PyQt5 import QtWidgets
|
from flatcamGUI.ColumnarFlowLayout import ColumnarFlowLayout
|
||||||
|
|
||||||
from flatcamGUI.preferences.OptionUI import OptionUI
|
from flatcamGUI.preferences.OptionUI import OptionUI
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
@@ -10,8 +10,7 @@ class PreferencesSectionUI(QtWidgets.QWidget):
|
|||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
self.layout = ColumnarFlowLayout() #QtWidgets.QHBoxLayout()
|
||||||
self.layout = QtWidgets.QHBoxLayout()
|
|
||||||
self.setLayout(self.layout)
|
self.setLayout(self.layout)
|
||||||
|
|
||||||
self.groups = self.build_groups()
|
self.groups = self.build_groups()
|
||||||
@@ -19,7 +18,6 @@ class PreferencesSectionUI(QtWidgets.QWidget):
|
|||||||
group.setMinimumWidth(250)
|
group.setMinimumWidth(250)
|
||||||
self.layout.addWidget(group)
|
self.layout.addWidget(group)
|
||||||
|
|
||||||
self.layout.addStretch()
|
|
||||||
|
|
||||||
def build_groups(self) -> [OptionsGroupUI]:
|
def build_groups(self) -> [OptionsGroupUI]:
|
||||||
return []
|
return []
|
||||||
@@ -34,6 +32,7 @@ class PreferencesSectionUI(QtWidgets.QWidget):
|
|||||||
def build_tab(self):
|
def build_tab(self):
|
||||||
scroll_area = QtWidgets.QScrollArea()
|
scroll_area = QtWidgets.QScrollArea()
|
||||||
scroll_area.setWidget(self)
|
scroll_area.setWidget(self)
|
||||||
|
scroll_area.setWidgetResizable(True)
|
||||||
return scroll_area
|
return scroll_area
|
||||||
|
|
||||||
def get_tab_id(self) -> str:
|
def get_tab_id(self) -> str:
|
||||||
|
|||||||
Reference in New Issue
Block a user