2021SC@SDUSC

Hippy项目源码分析第七周

layout/engine

Flex.h

enum:

  • FlexDirection: FlexDirectionRow | FlexDirectionRowReverse | FlexDirectionColumn | FlexDirectionColumnReverse
  • FlexWrapMode: FlexNoWrap | FlexWrap | FlexWrapReverse
  • FlexAlign: FlexAlignAuto | FlexAlignStart | FlexAlignCenter | FlexAlignEnd | FlexAlignStretch | FlexAlignBaseline | FlexAlignSpaceBetween | FlexAlignSpaceAround | FlexAlignSpaceEvenly
  • CSSDirection: CSSLeft = 0 | CSSTop | CSSRight | CSSBottom | CSSStart | CSSEnd | CSSHorizontal | CSSVertical | CSSAll | CSSNONE = -1 (用于获取填充的边距和主轴或横轴的位置)
  • Dimension: DimWidth = 0 | DimHeight
  • DisplayType: DisplayTypeFlex | DisplayTypeNone
  • OverflowType: OverflowVisible | OverflowHidden | OverflowScroll
  • NodeType: NodeTypeDefault | NodeTypeText
  • FlexLayoutAction: LayoutActionMeasureWidth = 1 | LayoutActionMeasureHeight = 2 | LayoutActionLayout = 3
  • MeasureMode: MeasureModeUndefined | MeasureModeExactly | MeasureModeMost

struct:

  • HPSize: float : { width | height }
  • HPLayout: float : { position[4] | cachedPosition[4] | dim[2] | margin[4] | padding[4] | border[4] | flexBaseSize | hypotheticalMainAxisMarginBoxSize | hypotheticalMainAxisSize } | bool : hadOverflow | HPDirection : direction
  • HPSizeMode: MeasureMode : { widthMeasureMode | heightMeasureMode }

以及一些判断方向的布尔型内联函数

Flexline.h

引用了Flex.h
定义了Flexline类,预定义了一些函数和变量

class FlexLine {
 public:
  explicit FlexLine(HPNodeRef container);
  void addItem(HPNodeRef item);
  bool isEmpty();
  FlexSign Sign() const {
    return sumHypotheticalMainSize < containerMainInnerSize ? PositiveFlexibility
                                                            : NegativeFlexibility;
  }
  void SetContainerMainInnerSize(float size) { containerMainInnerSize = size; }
  void FreezeViolations(std::vector<HPNode*>& violations);
  void FreezeInflexibleItems(FlexLayoutAction layoutAction);
  bool ResolveFlexibleLengths();
  void alignItems();

 public:
  std::vector<HPNodeRef> items;
  HPNodeRef flexContainer;
  // inner size in container main axis
  // 容器主轴内部尺寸
  float containerMainInnerSize;
  // accumulate item's Hypothetical MainSize in this line(include item's margin)
  // 在此行中累积项目的假想 MainSize(包括项目的边距)
  float sumHypotheticalMainSize;
  // accumulate flex grow of items in this line
  // 累积此行中项目的弹性增长
  float totalFlexGrow;
  float totalFlexShrink;
  // accumulate item's flexShrink * item 's mainSize
  // 累积 item 的 flexShrink * item 的 mainSize
  float totalWeightedFlexShrink;

  // this line's cross size:if this is a single line, may be determined by
  // container's style otherwise  determined by the largest item 's cross size.
  // 此行的交叉大小:如果这是单行,可能由容器的样式决定,否则由最大项目的交叉大小决定。
  float lineCrossSize;

  // init in FreezeInflexibleItems...
  float initialFreeSpace;
  float remainingFreeSpace;
};

Flexline.cpp

#include "FlexLine.h"
#include <cmath>
#include "HPNode.h"
#include "HPUtil.h"

FlexLine::FlexLine(HPNodeRef container) {
  ASSERT(container != nullptr);
  flexContainer = container;
  sumHypotheticalMainSize = 0;
  totalFlexGrow = 0;
  totalFlexShrink = 0;
  totalWeightedFlexShrink = 0;
  lineCrossSize = 0;
  initialFreeSpace = 0;
  remainingFreeSpace = 0;
  containerMainInnerSize = 0;
}

/*
 * add a item in flex line.
 * 在 flexline 中添加一个项目。
 */
void FlexLine::addItem(HPNodeRef item) {
  if (item == nullptr) {
    return;
  }

  sumHypotheticalMainSize += item->result.hypotheticalMainAxisMarginBoxSize;
  totalFlexGrow += item->style.flexGrow;
  totalFlexShrink += item->style.flexShrink;
  // For every unfrozen item on the line, multiply its flex shrink factor by its
  // inner flex base size, and note this as its scaled flex shrink factor.
  // 对于线上的每个未冻结项目,将其弹性收缩系数乘以其内部弹性基本尺寸,并将其记为缩放后的弹性收缩系数。
  // TODO(ianwang): inner flex base size ??????????
  totalWeightedFlexShrink += item->style.flexShrink * item->result.flexBaseSize;
  items.push_back(item);
}

bool FlexLine::isEmpty() {
  return items.size() == 0;
}

/*9.7. Resolving Flexible Lengths
 * 1. Determine the used flex factor. Sum the outer hypothetical main sizes of
 * all items on the line. If the sum is less than the flex container's inner
 * main size, use the flex grow factor for the rest of this algorithm;
 * otherwise, use the flex shrink factor.
 * 1. 确定使用的弹性系数。对线上所有项目的外部假设主要尺寸求和。
 * 如果总和小于 flex 容器的内部主要大小,则在此算法的其余部分使用 flex 增长因子;
 * 否则,使用弹性收缩系数。
 * 2. Size inflexible items. Freeze, setting its target main size to its
 * hypothetical main size's any item that has a flex factor of zero if using the
 * flex grow factor: any item that has a flex base size greater than its
 * hypothetical main size if using the flex shrink factor: any item that has a
 * flex base size smaller than its hypothetical main size
 * 2. 尺寸不灵活的项目。
 * 冻结,如果使用弹性增长因子,则将其目标主要尺寸设置为其假设的主要尺寸的任何弹性因子为零的项目:
 * 如果使用 flex 收缩因子,任何具有大于其假设主要尺寸的 flex 基本尺寸的项目:
 * 任何 flex 基本尺寸小于其假设主要尺寸的项目
 * 3. Calculate initial free space. Sum the outer sizes of all items on the
 * line, and subtract this from the flex container's inner main size. For frozen
 * items, use their outer target main size; for other items, use their outer
 * flex base size.
 * 3. 计算初始可用空间。
 * 将线上所有项目的外部尺寸相加,然后从 flex 容器的内部主要尺寸中减去它。
 * 对于冻结的项目,使用它们的外部目标主要尺寸;
 * 对于其他项目,请使用它们的外部弹性底座尺寸。
 */
void FlexLine::FreezeInflexibleItems(FlexLayoutAction layoutAction) {
  // no need use the resolveMainAxis of flexContainer
  // just get main axis from style
  // because it just calculate the size of items.
  // 无需使用 flexContainer 的 resolveMainAxis
  // 只需从样式中获取主轴
  // 因为它只是计算项目的大小。
  FlexDirection mainAxis = flexContainer->style.flexDirection;
  FlexSign flexSign = Sign();
  remainingFreeSpace = containerMainInnerSize - sumHypotheticalMainSize;
  std::vector<HPNodeRef> inFlexibleItems;
  for (size_t i = 0; i < items.size(); i++) {
    HPNodeRef item = items[i];
    if (layoutAction == LayoutActionLayout) {
      // if it in LayoutActionLayout state, reset frozen as false
      // resolve item main size again.
      // 如果它处于 LayoutActionLayout 状态,则将冻结重置为 false
      // 再次解决项目主要大小。
      item->isFrozen = false;
    }

    float flexFactor =
        flexSign == PositiveFlexibility ? item->style.flexGrow : item->style.flexShrink;
    if (flexFactor == 0 ||
        (flexSign == PositiveFlexibility &&
         item->result.flexBaseSize > item->result.hypotheticalMainAxisSize) ||
        (flexSign == NegativeFlexibility &&
         item->result.flexBaseSize < item->result.hypotheticalMainAxisSize)) {
      item->setLayoutDim(mainAxis, item->result.hypotheticalMainAxisSize);
      inFlexibleItems.push_back(item);
    }
  }

  // Recalculate the remaining free space and total flex grow , total flex
  // shrink
  // 重新计算剩余可用空间和总 flex 增长,总 flex 收缩
  FreezeViolations(inFlexibleItems);
  // Get Initial value here!!!
  // 在此处获取初始值!!!
  initialFreeSpace = remainingFreeSpace;
}

void FlexLine::FreezeViolations(std::vector<HPNode*>& violations) {
  // no need use the resolveMainAxis of flexContainer
  // just get main axis from style
  // because it just calculate the size of items.
  // 无需使用 flexContainer 的 resolveMainAxis
  // 只需从样式中获取主轴
  // 因为它只是计算项目的大小。
  FlexDirection mainAxis = flexContainer->style.flexDirection;
  for (size_t i = 0; i < violations.size(); i++) {
    HPNodeRef item = violations[i];
    if (item->isFrozen)
      continue;
    remainingFreeSpace -= (item->getLayoutDim(mainAxis) - item->result.hypotheticalMainAxisSize);
    totalFlexGrow -= item->style.flexGrow;
    totalFlexShrink -= item->style.flexShrink;
    totalWeightedFlexShrink -= item->style.flexShrink * item->result.flexBaseSize;
    totalWeightedFlexShrink = fmax(totalWeightedFlexShrink, 0.0);
    item->isFrozen = true;
  }
}

// Should be called in a loop until it returns false.
// 应该在循环中调用,直到它返回 false。
bool FlexLine::ResolveFlexibleLengths() {
  // no need use the resolveMainAxis of flexContainer
  // just get main axis from style
  // because it just calculate the size of items.
  FlexDirection mainAxis = flexContainer->style.flexDirection;
  float usedFreeSpace = 0;
  float totalViolation = 0;
  std::vector<HPNodeRef> minViolations;
  std::vector<HPNodeRef> maxViolations;

  FlexSign flexSign = Sign();
  float sumFlexFactors = (flexSign == PositiveFlexibility) ? totalFlexGrow : totalFlexShrink;
  /*  If the sum of the unfrozen flex items's flex factors is less than one,
   *  multiply the initial free space by this sum. If the magnitude of this
   *  value is less than the magnitude of the remaining free space,
   *  use this as the remaining free space.
   * 如果未冻结的弹性项目的弹性因子之和小于1,
   * 则将初始可用空间乘以该总和。
   * 如果此值的大小小于剩余可用空间的大小,则将其用作剩余可用空间。
   */
  if (sumFlexFactors > 0 && sumFlexFactors < 1) {
    float value = initialFreeSpace * sumFlexFactors;
    if (value < remainingFreeSpace) {
      remainingFreeSpace = value;
    }
  }

  for (size_t i = 0; i < items.size(); i++) {
    HPNodeRef item = items[i];
    if (item->isFrozen)
      continue;

    float extraSpace = 0;
    if (remainingFreeSpace > 0 && totalFlexGrow > 0 && flexSign == PositiveFlexibility) {
      extraSpace = remainingFreeSpace * item->style.flexGrow / totalFlexGrow;
    } else if (remainingFreeSpace < 0 && totalWeightedFlexShrink > 0 &&
               flexSign == NegativeFlexibility) {
      // For every unfrozen item on the line, multiply its flex shrink factor by
      // its inner flex base size, and note this as its scaled flex shrink
      // factor. Find the ratio of the item's scaled flex shrink factor to the
      // sum of the scaled flex shrink factors of all unfrozen items on the
      // line.
      /*
      *	对于线上的每个未冻结项目,
      *	将其弹性收缩系数乘以其内部弹性基本尺寸,
      *	并将其记为缩放后的弹性收缩系数。
      *	求项目的缩放后的弹性收缩系数与以下缩放系数的总和的比率所有未冻结的项目就行了。
      */

      extraSpace = remainingFreeSpace * item->style.flexShrink * item->result.flexBaseSize /
                   totalWeightedFlexShrink;
    }

    float violation = 0;
    if (std::isfinite(extraSpace)) {
      // Set the item's target main size to its flex base size minus a fraction
      // of the absolute value of the remaining free space proportional to the
      // ratio.
      // 将项目的目标主要大小设置为其 flex 基本大小减去与比例成比例的剩余可用空间绝对值的一小部分
      float itemMainSize = item->result.hypotheticalMainAxisSize + extraSpace;
      float adjustItemMainSize = item->boundAxis(mainAxis, itemMainSize);
      item->setLayoutDim(mainAxis, adjustItemMainSize);
      // use hypotheticalMainAxisSize  instead of item->boundAxis(mainAxis,
      // item->result.flexBasis);
      usedFreeSpace += adjustItemMainSize - item->result.hypotheticalMainAxisSize;
      violation = adjustItemMainSize - itemMainSize;
    }

    if (violation > 0) {
      minViolations.push_back(item);
    } else if (violation < 0) {
      maxViolations.push_back(item);
    }
    totalViolation += violation;
  }

  /*Zero
   * Freeze all items.
   * 冻结所有项目
   * Positive
   * Freeze all the items with min violations.
   * 用最小违规冻结所有项目
   * Negative
   * Freeze all the items with max violations.
   * 用最大违规冻结所有项目
   */
  if (totalViolation) {
    FreezeViolations(totalViolation < 0 ? maxViolations : minViolations);
  } else {
    remainingFreeSpace -= usedFreeSpace;
    // TODO(ianwang): FreezeViolations all
    // FreezeViolations(items);
  }

  return !totalViolation;
}

/*
 * 9.5. Main-Axis Alignment
 * 12.Distribute any remaining free space. For each flex line:
 * 分配任何剩余的可用空间。对于每条弹性线:
 * 1.If the remaining free space is positive and at least one main-axis margin
 * on this line is auto, distribute the free space equally among these margins.
 * Otherwise, set all auto margins to zero. 
 * 1.如果剩余可用空间为正值且该线上至少有一个主轴边距为自动,
 * 则在这些边距之间平均分配可用空间。否则,将所有自动边距设置为零。
 * 2.Align the items along the main-axis per justify-content.
 * 2.根据 justify-content 沿主轴对齐项目。
 */
void FlexLine::alignItems() {
  // need use the resolveMainAxis of flexContainer
  // because 'alignItems' calculate item's positions
  // which influenced by node's layout direction property.
  // 需要使用 flexContainer 的 resolveMainAxis,
  // 因为 'alignItems' 计算受节点布局方向属性影响的项目位置。
  FlexDirection mainAxis = flexContainer->resolveMainAxis();
  int itemsSize = items.size();
  // get autoMargin count,assure remainingFreeSpace Calculate again
  // 获取 autoMargin 计数,确保剩余可用空间再次计算
  remainingFreeSpace = containerMainInnerSize;
  int autoMarginCount = 0;
  for (int i = 0; i < itemsSize; i++) {
    HPNodeRef item = items[i];
    remainingFreeSpace -= (item->getLayoutDim(mainAxis) + item->getMargin(mainAxis));
    // TODO(ianwang): remainingFreeSpace may be a small float value , for example
    // : 1.52587891e-005 == 0.000015
    if (item->isAutoStartMargin(mainAxis)) {
      autoMarginCount++;
    }
    if (item->isAutoEndMargin(mainAxis)) {
      autoMarginCount++;
    }
  }

  // see HippyTest.align_items_center_child_without_margin_bigger_than_parent in
  // /tests folder remainingFreeSpace can be negative, < 0.
  // if(remainingFreeSpace < 0) {
  //  remainingFreeSpace = 0;
  // }

  float autoMargin = 0;
  if (remainingFreeSpace > 0 && autoMarginCount > 0) {
    autoMargin = remainingFreeSpace / autoMarginCount;
    remainingFreeSpace = 0;
  }

  for (int i = 0; i < itemsSize; i++) {
    HPNodeRef item = items[i];
    if (item->isAutoStartMargin(mainAxis)) {
      item->setLayoutStartMargin(mainAxis, autoMargin);
    } else {
      // For margin:: assign style value to result value at this place..
      // 对于 margin:: 在这个地方将样式值分配给结果值..
      item->setLayoutStartMargin(mainAxis, item->getStartMargin(mainAxis));
    }

    if (item->isAutoEndMargin(mainAxis)) {
      item->setLayoutEndMargin(mainAxis, autoMargin);
    } else {
      item->setLayoutEndMargin(mainAxis, item->getEndMargin(mainAxis));
    }
  }

  // 2. Align the items along the main-axis per justify-content.
  // 2. 根据 justify-content 沿主轴对齐项目。
  float offset = flexContainer->getStartPaddingAndBorder(mainAxis);
  HPStyle style = flexContainer->getStyle();
  float space = 0;
  switch (style.justifyContent) {
    case FlexAlignStart:
      break;
    case FlexAlignCenter:
      offset += remainingFreeSpace / 2;
      break;
    case FlexAlignEnd:
      offset += remainingFreeSpace;
      break;
    case FlexAlignSpaceBetween:
      space = remainingFreeSpace / (itemsSize - 1);
      break;
    case FlexAlignSpaceAround:
      space = remainingFreeSpace / itemsSize;
      offset += space / 2;
      break;
    case FlexAlignSpaceEvenly:
      space = remainingFreeSpace / (itemsSize + 1);
      offset += space;
      break;
    default:
      break;
  }

  // start end position set.
  // 开始结束位置设置。
  for (int i = 0; i < itemsSize; i++) {
    HPNodeRef item = items[i];
    offset += item->getLayoutStartMargin(mainAxis);
    item->setLayoutStartPosition(mainAxis, offset);
    item->setLayoutEndPosition(
        mainAxis, flexContainer->getLayoutDim(mainAxis) - item->getLayoutDim(mainAxis) - offset);
    offset += item->getLayoutDim(mainAxis) + item->getLayoutEndMargin(mainAxis) + space;
  }
}
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐