From 4bd3dfd5425703bef39b9e27ac100ea4844fcbd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 27 Dec 2019 18:09:17 +0100 Subject: [PATCH] [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 --- .../list/list_parameter_controller.cpp | 2 +- escher/include/escher/expression_table_cell.h | 2 +- .../expression_table_cell_with_pointer.h | 2 +- escher/include/escher/message_table_cell.h | 2 +- escher/include/escher/table_cell.h | 18 +- escher/src/expression_table_cell.cpp | 2 +- .../expression_table_cell_with_expression.cpp | 2 +- .../expression_table_cell_with_pointer.cpp | 2 +- .../message_table_cell_with_editable_text.cpp | 8 +- escher/src/table_cell.cpp | 176 ++++++++++++------ 10 files changed, 137 insertions(+), 79 deletions(-) diff --git a/apps/sequence/list/list_parameter_controller.cpp b/apps/sequence/list/list_parameter_controller.cpp index 56850b68f..b40cab658 100644 --- a/apps/sequence/list/list_parameter_controller.cpp +++ b/apps/sequence/list/list_parameter_controller.cpp @@ -12,7 +12,7 @@ ListParameterController::ListParameterController(::InputEventHandlerDelegate * i Shared::ListParameterController(listController, I18n::Message::SequenceColor, I18n::Message::DeleteSequence, this), m_typeCell(I18n::Message::SequenceType), m_initialRankCell(&m_selectableTableView, inputEventHandlerDelegate, this, I18n::Message::FirstTermIndex), - m_typeParameterController(this, listController, TableCell::Layout::Horizontal, Metric::CommonTopMargin, Metric::CommonRightMargin, + m_typeParameterController(this, listController, TableCell::Layout::HorizontalLeftOverlap, Metric::CommonTopMargin, Metric::CommonRightMargin, Metric::CommonBottomMargin, Metric::CommonLeftMargin) { } diff --git a/escher/include/escher/expression_table_cell.h b/escher/include/escher/expression_table_cell.h index 0a5a04efc..2a95f012f 100644 --- a/escher/include/escher/expression_table_cell.h +++ b/escher/include/escher/expression_table_cell.h @@ -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); diff --git a/escher/include/escher/expression_table_cell_with_pointer.h b/escher/include/escher/expression_table_cell_with_pointer.h index 7c9aa6c39..69a74ac37 100644 --- a/escher/include/escher/expression_table_cell_with_pointer.h +++ b/escher/include/escher/expression_table_cell_with_pointer.h @@ -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); diff --git a/escher/include/escher/message_table_cell.h b/escher/include/escher/message_table_cell.h index b87480201..788bfb1b1 100644 --- a/escher/include/escher/message_table_cell.h +++ b/escher/include/escher/message_table_cell.h @@ -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); diff --git a/escher/include/escher/table_cell.h b/escher/include/escher/table_cell.h index 6204c4d26..eded3bc2e 100644 --- a/escher/include/escher/table_cell.h +++ b/escher/include/escher/table_cell.h @@ -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; }; diff --git a/escher/src/expression_table_cell.cpp b/escher/src/expression_table_cell.cpp index 447b8f68c..bf3904053 100644 --- a/escher/src/expression_table_cell.cpp +++ b/escher/src/expression_table_cell.cpp @@ -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) { } diff --git a/escher/src/expression_table_cell_with_expression.cpp b/escher/src/expression_table_cell_with_expression.cpp index af2e7926f..785b201e6 100644 --- a/escher/src/expression_table_cell_with_expression.cpp +++ b/escher/src/expression_table_cell_with_expression.cpp @@ -3,7 +3,7 @@ #include ExpressionTableCellWithExpression::ExpressionTableCellWithExpression(Responder * parentResponder) : - ExpressionTableCell(parentResponder, Layout::Horizontal), + ExpressionTableCell(parentResponder, Layout::HorizontalLeftOverlap), m_accessoryExpressionView(1.0f, 0.5f, Palette::GreyDark, KDColorWhite) {} diff --git a/escher/src/expression_table_cell_with_pointer.cpp b/escher/src/expression_table_cell_with_pointer.cpp index 371ca28b7..2f51962e0 100644 --- a/escher/src/expression_table_cell_with_pointer.cpp +++ b/escher/src/expression_table_cell_with_pointer.cpp @@ -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); } } diff --git a/escher/src/message_table_cell_with_editable_text.cpp b/escher/src/message_table_cell_with_editable_text.cpp index b3b4ce53a..c7ec2314d 100644 --- a/escher/src/message_table_cell_with_editable_text.cpp +++ b/escher/src/message_table_cell_with_editable_text.cpp @@ -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); } diff --git a/escher/src/table_cell.cpp b/escher/src/table_cell.cpp index 45b1502fa..72cb5db62 100644 --- a/escher/src/table_cell.cpp +++ b/escher/src/table_cell.cpp @@ -3,6 +3,7 @@ #include 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); + } } }