[escher] TableCell: the subviews are layoutted horizontally or

vertically. We add an option to decide which subviews should overlap on
top of the other if there is not enough space
This commit is contained in:
Émilie Feral
2019-12-27 18:09:17 +01:00
committed by Léa Saviot
parent 5974149162
commit 4bd3dfd542
10 changed files with 137 additions and 79 deletions

View File

@@ -6,7 +6,7 @@
class ExpressionTableCell : public Responder, public TableCell {
public:
ExpressionTableCell(Responder * responder = nullptr, Layout layout = Layout::Horizontal);
ExpressionTableCell(Responder * responder = nullptr, Layout layout = Layout::HorizontalRightOverlap);
View * labelView() const override;
void setHighlighted(bool highlight) override;
void setLayout(Poincare::Layout layout);

View File

@@ -7,7 +7,7 @@
class ExpressionTableCellWithPointer : public ExpressionTableCell {
public:
ExpressionTableCellWithPointer(Responder * responder = nullptr, I18n::Message accessoryMessage = (I18n::Message)0, Layout layout = Layout::Horizontal);
ExpressionTableCellWithPointer(Responder * responder = nullptr, I18n::Message accessoryMessage = (I18n::Message)0, Layout layout = Layout::HorizontalRightOverlap);
View * accessoryView() const override;
void setHighlighted(bool highlight) override;
void setAccessoryMessage(I18n::Message messageBody);

View File

@@ -7,7 +7,7 @@
class MessageTableCell : public TableCell {
public:
MessageTableCell(I18n::Message label = (I18n::Message)0, const KDFont * font = KDFont::SmallFont, Layout layout = Layout::Horizontal);
MessageTableCell(I18n::Message label = (I18n::Message)0, const KDFont * font = KDFont::SmallFont, Layout layout = Layout::HorizontalLeftOverlap);
View * labelView() const override;
virtual void setHighlighted(bool highlight) override;
void setMessage(I18n::Message message);

View File

@@ -6,25 +6,31 @@
class TableCell : public HighlightCell {
public:
/* Layout enum class determines the way subviews are layouted.
* We can split the cell vertically or horizontally.
* We can choose which subviews frames are optimized (if there is not enough
* space for all subviews, which one is cropped). This case happens so far only
* for horizontally splitted cell, so we distinguish only these sub cases.
* TODO: implement VerticalTopOverlap, VerticalBottomlap? */
enum class Layout {
Vertical,
Horizontal
HorizontalLeftOverlap, // Label overlaps on SubAccessory which overlaps on Accessory
HorizontalRightOverlap, // Reverse
};
TableCell(Layout layout = Layout::Horizontal);
TableCell(Layout layout = Layout::HorizontalLeftOverlap);
virtual View * labelView() const;
virtual View * accessoryView() const;
virtual View * subAccessoryView() const;
void drawRect(KDContext * ctx, KDRect rect) const override;
constexpr static KDCoordinate k_labelMargin = 10;
constexpr static KDCoordinate k_accessoryMargin = 10;
protected:
virtual KDCoordinate labelMargin() const { return k_labelMargin; }
virtual KDCoordinate labelMargin() const { return Metric::TableCellHorizontalMargin; }
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews(bool force = false) override;
constexpr static KDCoordinate k_separatorThickness = Metric::CellSeparatorThickness;
constexpr static KDCoordinate k_verticalMargin = Metric::TableCellVerticalMargin;
constexpr static KDCoordinate k_horizontalMargin = Metric::TableCellHorizontalMargin;
private:
constexpr static KDCoordinate k_accessoryBottomMargin = 3;
Layout m_layout;
};

View File

@@ -6,7 +6,7 @@
ExpressionTableCell::ExpressionTableCell(Responder * parentResponder, Layout layout) :
Responder(parentResponder),
TableCell(layout),
m_labelExpressionView(this, k_labelMargin, 0, 0.0f, 0.5f, KDColorBlack, KDColorWhite)
m_labelExpressionView(this, k_horizontalMargin, 0, 0.0f, 0.5f, KDColorBlack, KDColorWhite)
{
}

View File

@@ -3,7 +3,7 @@
#include <assert.h>
ExpressionTableCellWithExpression::ExpressionTableCellWithExpression(Responder * parentResponder) :
ExpressionTableCell(parentResponder, Layout::Horizontal),
ExpressionTableCell(parentResponder, Layout::HorizontalLeftOverlap),
m_accessoryExpressionView(1.0f, 0.5f, Palette::GreyDark, KDColorWhite)
{}

View File

@@ -6,7 +6,7 @@ ExpressionTableCellWithPointer::ExpressionTableCellWithPointer(Responder * paren
ExpressionTableCell(parentResponder, layout),
m_accessoryView(KDFont::SmallFont, accessoryMessage, 0.0f, 0.5f, Palette::GreyDark, KDColorWhite)
{
if (layout == Layout::Horizontal) {
if (layout != Layout::Vertical) {
m_accessoryView.setAlignment(1.0f, 0.5f);
}
}

View File

@@ -52,11 +52,11 @@ void MessageTableCellWithEditableText::layoutSubviews(bool force) {
KDSize labelSize = labelView()->minimalSizeForOptimalDisplay();
/* Handle textfield that has no defined width (as their width evolves with
* the length of edited text */
textFieldSize = KDSize(bounds().width() - 2*k_separatorThickness - labelSize.width()-2*labelMargin()-k_accessoryMargin, textFieldSize.height());
textFieldSize = KDSize(bounds().width() - 2*k_separatorThickness - labelSize.width()-2*labelMargin()-k_horizontalMargin, textFieldSize.height());
m_textField.setFrame(KDRect(
bounds().width() - textFieldSize.width() - k_separatorThickness-k_accessoryMargin,
(bounds().height()-textFieldSize.height()-k_accessoryMargin)/2,
bounds().width() - textFieldSize.width() - k_separatorThickness-k_horizontalMargin,
(bounds().height()-textFieldSize.height()-k_horizontalMargin)/2,
textFieldSize.width(),
textFieldSize.height()+k_accessoryMargin),
textFieldSize.height()+k_horizontalMargin),
force);
}

View File

@@ -3,6 +3,7 @@
#include <escher/metric.h>
static inline KDCoordinate minCoordinate(KDCoordinate x, KDCoordinate y) { return x < y ? x : y; }
static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
TableCell::TableCell(Layout layout) :
HighlightCell(),
@@ -40,74 +41,125 @@ View * TableCell::subviewAtIndex(int index) {
* margins (like ExpressionView), sometimes the subview has no margins (like
* MessageView) which prevents us to handle margins only here. */
KDCoordinate withMargin(KDCoordinate length, KDCoordinate margin) {
return length == 0 ? 0 : length + margin;
}
void TableCell::layoutSubviews(bool force) {
/* TODO: this code is awful. However, this should handle multiples cases
* (subviews are not defined, margins are overriden...) */
KDCoordinate width = bounds().width();
KDCoordinate height = bounds().height();
View * label = labelView();
KDSize labelSize = label ? label->minimalSizeForOptimalDisplay() : KDSizeZero;
if (label) {
switch (m_layout) {
case Layout::Vertical:
{
KDCoordinate x = k_separatorThickness+labelMargin();
KDCoordinate y = k_separatorThickness+Metric::TableCellVerticalMargin;
label->setFrame(KDRect(
x,
y,
width-2*x,
minCoordinate(labelSize.height(), height-2*y)),
force);
break;
}
default:
{
KDCoordinate x = k_separatorThickness+labelMargin();
KDCoordinate y = k_separatorThickness;
label->setFrame(KDRect(
x,
k_separatorThickness,
minCoordinate(labelSize.width(), width-2*x),
height - 2*y),
force);
break;
}
}
}
View * accessory = accessoryView();
if (accessory) {
KDSize accessorySize = accessory->minimalSizeForOptimalDisplay();
switch (m_layout) {
case Layout::Vertical:
{
KDCoordinate x = k_separatorThickness+k_accessoryMargin;
accessory->setFrame(KDRect(
x,
height-k_separatorThickness-accessorySize.height()-k_accessoryBottomMargin,
width-2*x,
accessorySize.height()),
force);
break;
}
default:
// In some cases, the accessory view cannot take all the size it can
KDCoordinate wantedX = width-accessorySize.width()-k_separatorThickness-k_accessoryMargin;
KDCoordinate minX = label ? label->bounds().x()+labelSize.width()+labelMargin()+k_separatorThickness+k_accessoryMargin : k_accessoryMargin;
KDCoordinate x = minX < wantedX ? wantedX : minX;
accessory->setFrame(KDRect(
x,
k_separatorThickness,
minCoordinate(accessorySize.width(), width - x),
height-2*k_separatorThickness),
force);
break;
}
}
View * subAccessory = subAccessoryView();
if (subAccessory && accessory) {
KDSize accessorySize = accessory->minimalSizeForOptimalDisplay();
KDSize subAccessorySize = subAccessory->minimalSizeForOptimalDisplay();
subAccessory->setFrame(KDRect(width-k_separatorThickness-k_accessoryMargin-accessorySize.width()-subAccessorySize.width(), k_separatorThickness,
subAccessorySize.width(), height-2*k_separatorThickness), force);
KDSize labelSize = label ? label->minimalSizeForOptimalDisplay() : KDSizeZero;
KDSize accessorySize = accessory ? accessory->minimalSizeForOptimalDisplay() : KDSizeZero;
KDSize subAccessorySize = subAccessory ? subAccessory->minimalSizeForOptimalDisplay() : KDSizeZero;
if (m_layout == Layout::Vertical) {
/*
* Vertically:
* ----------------
* ----------------
* Line separator
* ----------------
* k_verticalMargin
* ----------------
* LABEL
* ----------------
* k_verticalMargin
* ----------------
* .
* . [White space if possible, otherwise LABEL overlaps SUBACCESSORY and so on]
* .
* ----------------
* SUBACCESSORY
* ----------------
* ACCESSORY
* ----------------
* k_verticalMargin
* ----------------
* Line separator
* ----------------
* ----------------
*
*
* Horizontally:
* || Line separator | margin* | SUBVIEW | margin* | Line separator ||
*
* * = margin can either be labelMargin() or k_horizontalMargin depending on the subview
*
* */
KDCoordinate horizontalMargin = k_separatorThickness + labelMargin();
KDCoordinate y = k_separatorThickness;
if (label) {
y += k_verticalMargin;
KDCoordinate labelHeight = minCoordinate(labelSize.height(), height - y - k_separatorThickness - k_verticalMargin);
label->setFrame(KDRect(horizontalMargin, y, width-2*horizontalMargin, labelHeight), force);
y += labelHeight + k_verticalMargin;
}
horizontalMargin = k_separatorThickness + k_horizontalMargin;
y = maxCoordinate(y, height - k_separatorThickness - withMargin(accessorySize.height(), Metric::TableCellVerticalMargin) - withMargin(subAccessorySize.height(), 0));
if (subAccessory) {
KDCoordinate subAccessoryHeight = minCoordinate(subAccessorySize.height(), height - y - k_separatorThickness - Metric::TableCellVerticalMargin);
accessory->setFrame(KDRect(horizontalMargin, y, width - 2*horizontalMargin, subAccessoryHeight), force);
y += subAccessoryHeight;
}
y = maxCoordinate(y, height - k_separatorThickness - withMargin(accessorySize.height(), Metric::TableCellVerticalMargin));
if (accessory) {
KDCoordinate accessoryHeight = minCoordinate(accessorySize.height(), height - y - k_separatorThickness - Metric::TableCellVerticalMargin);
accessory->setFrame(KDRect(horizontalMargin, y, width - 2*horizontalMargin, accessoryHeight), force);
}
} else {
/*
* Vertically:
* ----------------
* ----------------
* Line separator
* ----------------
* SUBVIEW
* ----------------
* Line separator
* ----------------
* ----------------
*
* Horizontally:
* || Line separator | Label margin | LABEL | Label margin | ...
* [ White space if possible otherwise the overlap can be from left to
* right subviews or the contrary ]
*
* ... | SUBACCESSORY | ACCESSORY | k_horizontalMargin | Line separator ||
*
* */
KDCoordinate verticalMargin = k_separatorThickness;
KDCoordinate x = 0;
KDCoordinate labelX = k_separatorThickness + labelMargin();
KDCoordinate subAccessoryX = maxCoordinate(k_separatorThickness+k_horizontalMargin, width - k_separatorThickness - withMargin(accessorySize.width(), k_horizontalMargin) - withMargin(subAccessorySize.width(), 0));
KDCoordinate accessoryX = maxCoordinate(k_separatorThickness+k_horizontalMargin, width - k_separatorThickness - withMargin(accessorySize.width(), k_horizontalMargin));
if (label) {
x = labelX;
KDCoordinate labelWidth = minCoordinate(labelSize.width(), width - x - k_separatorThickness - labelMargin());
if (m_layout == Layout::HorizontalRightOverlap) {
labelWidth = minCoordinate(labelWidth, subAccessoryX - x - labelMargin());
}
label->setFrame(KDRect(x, verticalMargin, labelWidth, height-2*verticalMargin), force);
x += labelWidth + labelMargin();
}
if (subAccessory) {
x = maxCoordinate(x, subAccessoryX);
KDCoordinate subAccessoryWidth = minCoordinate(subAccessorySize.width(), width - x - k_separatorThickness - k_horizontalMargin);
if (m_layout == Layout::HorizontalRightOverlap) {
subAccessoryWidth = minCoordinate(subAccessoryWidth, accessoryX - x);
}
subAccessory->setFrame(KDRect(x, verticalMargin, subAccessoryWidth, height-2*verticalMargin), force);
x += subAccessoryWidth;
}
if (accessory) {
x = maxCoordinate(x, accessoryX);
KDCoordinate accessoryWidth = minCoordinate(accessorySize.width(), width - x - k_separatorThickness - k_horizontalMargin);
accessory->setFrame(KDRect(x, verticalMargin, accessoryWidth, height-2*verticalMargin), force);
}
}
}