Commit 92d85e68 authored by Paul Asmuth's avatar Paul Asmuth
Browse files

update the legend element

parent 91212ba0
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -27,12 +27,12 @@
#include "elements/plot/bars.h"
#include "elements/plot/grid.h"
#include "elements/plot/labels.h"
#include "elements/chart/legend.h"
#include "elements/plot/lines.h"
#include "elements/chart/linechart.h"
#include "elements/plot/points.h"
#include "elements/chart/scatterplot.h"
#include "elements/layout/box.h"
#include "elements/legend.h"
#include "elements/legend/item.h"

#include <iostream>
@@ -51,10 +51,8 @@ struct fviz_s {
fviz_t* fviz_init() {
  auto ctx = std::make_unique<fviz_t>();
  auto elems = &ctx->env.element_map;
  element_bind(elems, "chart/legend", bind(elements::chart::legend::build, _1, _2, _3));
  element_bind(elems, "chart/linechart", bind(elements::chart::linechart::build, _1, _2, _3));
  element_bind(elems, "chart/scatterplot", bind(elements::chart::scatterplot::build, _1, _2, _3));
  element_bind(elems, "layout/box", bind(elements::layout::box::build, _1, _2, _3));
  element_bind(elems, "plot", bind(elements::plot::build, _1, _2, _3));
  element_bind(elems, "plot/areas", bind(elements::plot::areas::build, _1, _2, _3));
  element_bind(elems, "plot/axis", bind(elements::plot::axis::build, _1, _2, _3));
@@ -63,7 +61,9 @@ fviz_t* fviz_init() {
  element_bind(elems, "plot/labels", bind(elements::plot::labels::build, _1, _2, _3));
  element_bind(elems, "plot/lines", bind(elements::plot::lines::build, _1, _2, _3));
  element_bind(elems, "plot/points", bind(elements::plot::points::build, _1, _2, _3));
  element_bind(elems, "legend", bind(elements::legend::build, _1, _2, _3));
  element_bind(elems, "legend/item", bind(elements::legend::item::build, _1, _2, _3));
  element_bind(elems, "layout/box", bind(elements::layout::box::build, _1, _2, _3));
  element_bind(elems, "text", bind(elements::text::build, _1, _2, _3));
  return ctx.release();
}
+1 −1
Original line number Diff line number Diff line
@@ -134,7 +134,7 @@ ReturnCode expr_parse(
      }

      case ')': {
        if (stack.empty()) {
        if (stack.size() < 2) {
          return error(ERROR, "unbalanced parens");
        }

elements/chart/legend.cc

deleted100644 → 0
+0 −388
Original line number Diff line number Diff line
/**
 * This file is part of the "fviz" project
 *   Copyright (c) 2018 Paul Asmuth
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "legend.h"

#include "data.h"
#include "environment.h"
#include "layout.h"
#include "scale.h"
#include "sexpr.h"
#include "sexpr_conv.h"
#include "sexpr_util.h"
#include "graphics/path.h"
#include "graphics/brush.h"
#include "graphics/text.h"
#include "graphics/layout.h"

using namespace std::placeholders;

namespace fviz::elements::chart::legend {

const double kDefaultLabelFontSizeEM = 1;
const double kDefaultPaddingHorizEM = 1.8;
const double kDefaultPaddingVertEM = 1.4;
const double kDefaultItemPaddingHorizEM = 2.4;
const double kDefaultItemPaddingVertEM = 1.0;
const std::string LEGEND_DEFAULT = "default";

enum class LegendPlacement {
  OFF,
  INSIDE,
  OUTSIDE
};

using LegendItemList = std::vector<std::string>;

struct LegendConfig {
  LegendConfig();
  std::string key;
  Color text_color;
  Color border_color;
  FontInfo font;
  Measure padding_horiz;
  Measure padding_vert;
  Measure padding_item_horiz;
  Measure padding_item_vert;
  Measure margins[4];
  Measure item_margins[4];
  LegendPlacement placement;
  HAlign position_horiz;
  VAlign position_vert;
  std::string title;
  std::vector<std::string> items;
  std::vector<Color> colors;
  LayoutSettings layout;
};

LegendConfig::LegendConfig() :
    key(LEGEND_DEFAULT),
    placement(LegendPlacement::INSIDE),
    position_horiz(HAlign::RIGHT),
    position_vert(VAlign::TOP) {}

ReturnCode legend_layout_items(
    const LegendConfig& legend,
    const std::vector<std::string>& legend_items,
    Point origin,
    std::function<ReturnCode (size_t idx, Point p)> on_label,
    const Layer* layer,
    Rectangle* bbox) {
  auto font_size = from_em(kDefaultLabelFontSizeEM, layer->font_size);
  auto padding_item_right = measure_or(
      legend.item_margins[1],
      from_em(kDefaultItemPaddingHorizEM, font_size));

  double point_size = 5; // TODO
  double line_height = font_size; // TODO

  double sx = origin.x;
  double sy = origin.y;

  for (size_t idx = 0; idx < legend_items.size(); ++idx) {
    const auto& label_text = legend_items[idx];

    if (on_label) {
      if (auto rc = on_label(idx, Point(sx, sy + line_height / 2)); !rc) {
        return rc;
      }
    }

    TextStyle style;
    style.font = legend.font;
    style.font_size = font_size;

    Rectangle label_bbox;
    auto rc = text::text_measure_span(
        label_text,
        style.font,
        style.font_size,
        layer->dpi,
        layer->text_shaper.get(),
        &label_bbox);

    if (rc != OK) {
      return rc;
    }

    sx += point_size * 2.8; // TODO
    sx += label_bbox.w;

    if (idx + 1 < legend_items.size()) {
      sx += padding_item_right;
    }
  }

  sy += line_height;

  if (bbox) {
    *bbox = Rectangle(origin.x, origin.y, sx - origin.x, sy - origin.y);
  }

  return OK;
}

ReturnCode legend_draw_inside(
    const LegendConfig& legend,
    const Rectangle& bbox,
    Layer* layer) {
  auto font_size = from_em(kDefaultLabelFontSizeEM, layer->font_size);

  double padding_left = measure_or(
      legend.margins[3],
      from_em(kDefaultPaddingHorizEM, font_size));

  double padding_right = measure_or(
      legend.margins[1],
      from_em(kDefaultPaddingHorizEM, font_size));

  double padding_top = measure_or(
      legend.margins[0],
      from_em(kDefaultPaddingVertEM, font_size));

  double padding_bottom = measure_or(
      legend.margins[2],
      from_em(kDefaultPaddingVertEM, font_size));

  double padding_item_right = measure_or(
      legend.item_margins[1],
      from_em(kDefaultItemPaddingHorizEM, font_size));

  double point_size = 5; // TODO
  auto draw_label = [&] (size_t idx, Point pos) {
    const auto& label_text = legend.items[idx];

    const auto& color = legend.colors.empty()
        ? Color{}
        : legend.colors[idx % legend.colors.size()];

    {
      FillStyle style;
      style.color = color;
      Path path;
      path.moveTo(pos.x + point_size + point_size / 2, pos.y);
      path.arcTo(pos.x + point_size / 2, pos.y, point_size, 0, M_PI * 2);
      fillPath(layer, path, style);
      pos.x += point_size * 2.8; // TODO
    }

    {
      TextStyle style;
      style.color = legend.text_color;
      style.font = legend.font;
      style.font_size = font_size;

      if (auto rc = drawTextLabel(
            label_text,
            pos,
            HAlign::LEFT,
            VAlign::CENTER,
            style,
            layer); rc != OK) {
        return rc;
      }
    }

    return OK;
  };

  Rectangle content_bbox;
  if (auto rc = legend_layout_items(
      legend,
      legend.items,
      Point(0, 0),
      nullptr,
      layer,
      &content_bbox);
      !rc) {
    return rc;
  }

  Point origin(0, 0);

  switch (legend.position_horiz) {
    case HAlign::LEFT:
      origin.x = std::min(
          bbox.x + padding_left,
          bbox.x + bbox.w);
      break;
    case HAlign::RIGHT:
      origin.x = std::max(
          bbox.x + bbox.w - content_bbox.w - padding_right,
          bbox.x);
      break;
    case HAlign::CENTER:
      origin.x = bbox.x + bbox.w / 2 - content_bbox.w / 2;
      break;
  }

  switch (legend.position_vert) {
    case VAlign::TOP:
      origin.y = bbox.y + padding_top;
      break;
    case VAlign::BOTTOM:
      origin.y = bbox.y + bbox.h - content_bbox.h - padding_bottom;
      break;
    case VAlign::CENTER:
      origin.y = bbox.y + bbox.h / 2;
      break;
  }

  if (auto rc = legend_layout_items(
      legend,
      legend.items,
      origin,
      draw_label,
      layer,
      nullptr);
      !rc) {
    return rc;
  }

  return OK;
}

ReturnCode legend_draw(
    std::shared_ptr<LegendConfig> config,
    const LayoutInfo& layout,
    Layer* layer) {
  switch (config->placement) {
    case LegendPlacement::INSIDE:
      return legend_draw_inside(*config, layout.content_box, layer);
    case LegendPlacement::OUTSIDE:
    case LegendPlacement::OFF:
    default:
      return ERROR; // TODO: not implemented
  }
}

ReturnCode legend_configure_position(
    const Expr* expr,
    LegendPlacement* placement,
    HAlign* position_horiz,
    VAlign* position_vert) {
  if (!expr || !expr_is_list(expr)) {
    return errorf(
        ERROR,
        "invalid argument; expected a list but got: {}",
        "..."); // FIXME
  }

  bool position_horiz_set = false;
  bool position_vert_set = false;

  for (expr = expr_get_list(expr); expr; expr = expr_next(expr)) {
    if (expr_is_value(expr, "off")) {
      *placement = LegendPlacement::OFF;
      return OK;
    }

    if (expr_is_value(expr, "inside")) {
      *placement = LegendPlacement::INSIDE;
      continue;
    }

    if (expr_is_value(expr, "outside")) {
      *placement = LegendPlacement::OUTSIDE;
      continue;
    }

    if (expr_is_value(expr, "top")) {
      *position_vert = VAlign::TOP;
      position_vert_set = true;
      continue;
    }

    if (expr_is_value(expr, "bottom")) {
      *position_vert = VAlign::BOTTOM;
      position_vert_set = true;
      continue;
    }

    if (expr_is_value(expr, "left")) {
      *position_horiz = HAlign::LEFT;
      position_horiz_set = true;
      continue;
    }

    if (expr_is_value(expr, "right")) {
      *position_horiz = HAlign::RIGHT;
      position_horiz_set = true;
      continue;
    }

    if (expr_is_value(expr, "center")) {
      if (!position_horiz_set) *position_horiz = HAlign::CENTER;
      if (!position_vert_set) *position_vert = VAlign::CENTER;
      continue;
    }

    return ERROR;
  }

  return OK;
}

ReturnCode build(
    const Environment& env,
    const Expr* expr,
    ElementRef* elem) {
  /* inherit defaults */
  auto config = std::make_shared<LegendConfig>();
  config->font = env.font;
  config->border_color = env.border_color;
  config->text_color = env.text_color;

  /* parse exprerties */
  auto config_rc = expr_walk_map(expr_next(expr), {
    {
      "position",
      bind(
          &legend_configure_position,
          _1,
          &config->placement,
          &config->position_horiz,
          &config->position_vert)
    },
    {"title", bind( &expr_to_string, _1, &config->title)},
    {"text-color", bind(&expr_to_color, _1, &config->text_color)},
    {"border-color", bind(&expr_to_color, _1, &config->border_color)},
    {
      "item-margin",
      expr_calln_fn({
          bind(&expr_to_measure, _1, &config->item_margins[0]),
          bind(&expr_to_measure, _1, &config->item_margins[1]),
          bind(&expr_to_measure, _1, &config->item_margins[2]),
          bind(&expr_to_measure, _1, &config->item_margins[3])
      })
    },
    {"item-margin-top", bind(&expr_to_measure, _1, &config->item_margins[0])},
    {"item-margin-right", bind(&expr_to_measure, _1, &config->item_margins[1])},
    {"item-margin-bottom", bind(&expr_to_measure, _1, &config->item_margins[2])},
    {"item-margin-left", bind(&expr_to_measure, _1, &config->item_margins[3])},
    {"color", expr_tov_fn<Color>(bind(&expr_to_color, _1, _2), &config->colors)},
    {"colors", expr_tov_fn<Color>(bind(&expr_to_color, _1, _2), &config->colors)},
    {"items", bind(&data_load_strings, _1, &config->items)},
  });

  if (!config_rc) {
    return config_rc;
  }

  *elem = std::make_shared<Element>();
  (*elem)->draw = bind(&legend_draw, config, _1, _2);
  return OK;
}

} // namespace fviz::elements::chart::legend

elements/legend.cc

0 → 100644
+450 −0
Original line number Diff line number Diff line
/**
 * This file is part of the "fviz" project
 *   Copyright (c) 2018 Paul Asmuth
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "legend.h"

#include "data.h"
#include "environment.h"
#include "layout.h"
#include "scale.h"
#include "sexpr.h"
#include "sexpr_conv.h"
#include "sexpr_util.h"
#include "graphics/path.h"
#include "graphics/brush.h"
#include "graphics/text.h"
#include "graphics/layout.h"

using namespace std::placeholders;

namespace fviz::elements::legend {

struct LegendConfig {
  LegendConfig();
  HAlign position_horiz;
  VAlign position_vert;
  Measure item_row_padding;
  Measure item_column_padding;
  std::array<Measure, 4> margins;
  std::array<Measure, 4> padding;
  std::array<StrokeStyle, 4> borders;
  std::optional<Color> background;
  std::vector<ElementRef> items;
};

LegendConfig::LegendConfig() :
    position_horiz(HAlign::LEFT),
    position_vert(VAlign::TOP) {}


void legend_normalize(
    std::shared_ptr<LegendConfig> config,
    const Layer& layer) {
  for (auto& m : config->margins) {
    convert_unit_typographic(layer.dpi, layer.font_size, &m);
  }

  for (auto& m : config->padding) {
    convert_unit_typographic(layer.dpi, layer.font_size, &m);
  }

  convert_unit_typographic(layer.dpi, layer.font_size, &config->item_row_padding);
  convert_unit_typographic(layer.dpi, layer.font_size, &config->item_column_padding);
}

ReturnCode legend_layout_item_rows(
    const LegendConfig& config,
    const Layer& layer,
    const std::optional<double> max_width,
    const std::optional<double> max_height,
    double* min_width,
    double* min_height) {
  /* calculate vertical size */
  double m_width = 0;
  double m_height = 0;

  for (const auto& e : config.items) {
    if (!e->size_hint) {
      continue; // FIXME warn
    }

    auto p_width = max_width;
    auto p_height = max_height;
    if (p_height) {
      p_height = *p_height - m_height;
    }

    double e_width = 0;
    double e_height = 0;
    if (auto rc = e->size_hint(layer, p_width, p_height, &e_width, &e_height); !rc) {
      return rc;
    }

    m_width = std::max(m_width, e_width);
    m_height += e_height;
  }

  /* add item padding */
  if (config.items.size() > 1) {
    m_height += config.item_row_padding * (config.items.size() - 1);
  }


  *min_width = m_width;
  *min_height = m_height;
  return OK;
}

ReturnCode legend_layout(
    std::shared_ptr<LegendConfig> config,
    const Layer& layer,
    const std::optional<double> max_width,
    const std::optional<double> max_height,
    double* min_width,
    double* min_height) {
  /* convert units  */
  legend_normalize(config, layer);

  /* add item extents */
  auto layout_rc =
      legend_layout_item_rows(
          *config,
          layer,
          max_width,
          max_height,
          min_width,
          min_height);

  if (!layout_rc) {
    return layout_rc;
  }

  /* add padding */
  *min_width += config->padding[1];
  *min_width += config->padding[3];
  *min_height += config->padding[0];
  *min_height += config->padding[2];

  return OK;
}

ReturnCode legend_draw_borders(
    const StrokeStyle* borders,
    const Rectangle& bbox,
    Layer* layer) {
  /* draw top border  */
  if (borders[0].line_width > 0) {
    StrokeStyle border_style;
    border_style.line_width = borders[0].line_width;
    border_style.color = borders[0].color;

    strokeLine(
        layer,
        Point(bbox.x, bbox.y),
        Point(bbox.x + bbox.w, bbox.y),
        border_style);
  }

  /* draw right border  */
  if (borders[1].line_width > 0) {
    StrokeStyle border_style;
    border_style.line_width = borders[1].line_width;
    border_style.color = borders[1].color;

    strokeLine(
        layer,
        Point(bbox.x + bbox.w, bbox.y),
        Point(bbox.x + bbox.w, bbox.y + bbox.h),
        border_style);
  }

  /* draw top border  */
  if (borders[2].line_width > 0) {
    StrokeStyle border_style;
    border_style.line_width = borders[2].line_width;
    border_style.color = borders[2].color;

    strokeLine(
        layer,
        Point(bbox.x, bbox.y + bbox.h),
        Point(bbox.x + bbox.w, bbox.y + bbox.h),
        border_style);
  }

  /* draw left border  */
  if (borders[3].line_width > 0) {
    StrokeStyle border_style;
    border_style.line_width = borders[3].line_width;
    border_style.color = borders[3].color;

    strokeLine(
        layer,
        Point(bbox.x, bbox.y),
        Point(bbox.x, bbox.y + bbox.h),
        border_style);
  }
  return OK;
}

ReturnCode legend_draw_items(
    const LegendConfig& config,
    const Rectangle& bbox,
    Layer* layer) {
  double voffset = 0;

  for (const auto& e : config.items) {
    double pw = bbox.w;
    double ph = bbox.h;
    double ew = 0;
    double eh = 0;
    if (auto rc = e->size_hint(*layer, pw, ph, &ew, &eh); !rc) {
      return rc;
    }

    LayoutInfo layout;
    layout.content_box.x = bbox.x;
    layout.content_box.y = bbox.y + voffset;
    layout.content_box.w = bbox.w;
    layout.content_box.h = std::max(0.0, eh);

    if (auto rc = e->draw(layout, layer); !rc) {
      return rc;
    }

    voffset += eh;
    voffset += config.item_row_padding;
  }

  return OK;
}

ReturnCode legend_draw(
    std::shared_ptr<LegendConfig> config,
    const LayoutInfo& layout,
    Layer* layer) {
  /* convert units  */
  legend_normalize(config, *layer);

  /* calculate boxes */
  auto parent_box = layout_margin_box(
      layout.content_box,
      config->margins[0],
      config->margins[1],
      config->margins[2],
      config->margins[3]);

  Rectangle border_box;
  {
    auto pw = parent_box.w;
    auto ph = parent_box.h;
    double ew = 0;
    double eh = 0;
    if (auto rc = legend_layout(config, *layer, pw, ph, &ew, &eh); !rc) {
      return rc;
    }

    border_box.x = parent_box.x;
    border_box.y = parent_box.y;
    border_box.h = std::min(eh, parent_box.h);
    border_box.w = std::min(ew, parent_box.w);
  }

  auto content_box = layout_margin_box(
      border_box,
      config->padding[0],
      config->padding[1],
      config->padding[2],
      config->padding[3]);

  /* draw background */
  if (config->background) {
    const auto& bg_box = border_box;
    FillStyle bg_fill;
    bg_fill.color = *config->background;

    fillRectangle(
        layer,
        Point(bg_box.x, bg_box.y),
        bg_box.w,
        bg_box.h,
        bg_fill);
  }

  /* draw borders */
  if (auto rc = legend_draw_borders(config->borders.data(), border_box, layer); !rc) {
    return rc;
  }

  /* draw items */
  if (auto rc = legend_draw_items(*config, content_box, layer); !rc) {
    return rc;
  }

  return OK;
}

ReturnCode legend_configure_position(
    const Expr* expr,
    HAlign* position_horiz,
    VAlign* position_vert) {
  if (!expr || !expr_is_list(expr)) {
    return errorf(
        ERROR,
        "invalid argument; expected a list but got: {}",
        expr_inspect(expr));
  }

  bool position_horiz_set = false;
  bool position_vert_set = false;

  for (expr = expr_get_list(expr); expr; expr = expr_next(expr)) {
    if (expr_is_value(expr, "top")) {
      *position_vert = VAlign::TOP;
      position_vert_set = true;
      continue;
    }

    if (expr_is_value(expr, "bottom")) {
      *position_vert = VAlign::BOTTOM;
      position_vert_set = true;
      continue;
    }

    if (expr_is_value(expr, "left")) {
      *position_horiz = HAlign::LEFT;
      position_horiz_set = true;
      continue;
    }

    if (expr_is_value(expr, "right")) {
      *position_horiz = HAlign::RIGHT;
      position_horiz_set = true;
      continue;
    }

    if (expr_is_value(expr, "center")) {
      if (!position_horiz_set) *position_horiz = HAlign::CENTER;
      if (!position_vert_set) *position_vert = VAlign::CENTER;
      continue;
    }

    return ERROR;
  }

  return OK;
}

ReturnCode build(
    const Environment& env,
    const Expr* expr,
    ElementRef* elem) {
  /* inherit defaults */
  auto config = std::make_shared<LegendConfig>();
  config->item_row_padding = from_em(.3);
  config->item_column_padding = from_em(1);
  config->margins = std::array<Measure, 4>{from_em(1), from_em(1), from_em(1), from_em(1)};
  config->padding = std::array<Measure, 4>{from_em(.8), from_em(1), from_em(.8), from_em(1)};
  for (size_t i = 0; i < 4; ++i) {
    config->borders[i].line_width = from_pt(1);
    config->borders[i].color = env.border_color;
  }

  /* parse exprerties */
  auto config_rc = expr_walk_map(expr_next(expr), {
    {
      "position",
      bind(
          &legend_configure_position,
          _1,
          &config->position_horiz,
          &config->position_vert)
    },
    {"item-row-padding", bind(&expr_to_measure, _1, &config->item_row_padding)},
    {"item-column-padding", bind(&expr_to_measure, _1, &config->item_column_padding)},
    {"items", bind(&element_build_list, env, _1, &config->items)},
    {
      "padding",
      expr_calln_fn({
        bind(&expr_to_measure, _1, &config->padding[0]),
        bind(&expr_to_measure, _1, &config->padding[1]),
        bind(&expr_to_measure, _1, &config->padding[2]),
        bind(&expr_to_measure, _1, &config->padding[3]),
      })
    },
    {"padding-top", bind(&expr_to_measure, _1, &config->padding[0])},
    {"padding-right", bind(&expr_to_measure, _1, &config->padding[1])},
    {"padding-bottom", bind(&expr_to_measure, _1, &config->padding[2])},
    {"padding-left", bind(&expr_to_measure, _1, &config->padding[3])},
    {
      "margin",
      expr_calln_fn({
        bind(&expr_to_measure, _1, &config->margins[0]),
        bind(&expr_to_measure, _1, &config->margins[1]),
        bind(&expr_to_measure, _1, &config->margins[2]),
        bind(&expr_to_measure, _1, &config->margins[3]),
      })
    },
    {"margin-top", bind(&expr_to_measure, _1, &config->margins[0])},
    {"margin-right", bind(&expr_to_measure, _1, &config->margins[1])},
    {"margin-bottom", bind(&expr_to_measure, _1, &config->margins[2])},
    {"margin-left", bind(&expr_to_measure, _1, &config->margins[3])},
    {
      "border",
      expr_calln_fn({
        bind(&expr_to_stroke_style, _1, &config->borders[0]),
        bind(&expr_to_stroke_style, _1, &config->borders[1]),
        bind(&expr_to_stroke_style, _1, &config->borders[2]),
        bind(&expr_to_stroke_style, _1, &config->borders[3])
      })
    },
    {
      "border-color",
      expr_calln_fn({
        bind(&expr_to_color, _1, &config->borders[0].color),
        bind(&expr_to_color, _1, &config->borders[1].color),
        bind(&expr_to_color, _1, &config->borders[2].color),
        bind(&expr_to_color, _1, &config->borders[3].color),
      })
    },
    {"border-top-color", bind(&expr_to_color, _1, &config->borders[0].color)},
    {"border-right-color", bind(&expr_to_color, _1, &config->borders[1].color)},
    {"border-bottom-color", bind(&expr_to_color, _1, &config->borders[2].color)},
    {"border-left-color", bind(&expr_to_color, _1, &config->borders[3].color)},
    {
      "border-width",
      expr_calln_fn({
        bind(&expr_to_measure, _1, &config->borders[0].line_width),
        bind(&expr_to_measure, _1, &config->borders[1].line_width),
        bind(&expr_to_measure, _1, &config->borders[2].line_width),
        bind(&expr_to_measure, _1, &config->borders[3].line_width),
      })
    },
    {"border-top-width", bind(&expr_to_measure, _1, &config->borders[0].line_width)},
    {"border-right-width", bind(&expr_to_measure, _1, &config->borders[1].line_width)},
    {"border-bottom-width", bind(&expr_to_measure, _1, &config->borders[2].line_width)},
    {"border-left-width", bind(&expr_to_measure, _1, &config->borders[3].line_width)},
    {"background", bind(&expr_to_color_opt, _1, &config->background)},
  });

  if (!config_rc) {
    return config_rc;
  }

  *elem = std::make_shared<Element>();
  (*elem)->draw = bind(&legend_draw, config, _1, _2);
  (*elem)->size_hint = bind(&legend_layout, config, _1, _2, _3, _4, _5);
  return OK;
}

} // namespace fviz::elements::legend
+2 −2
Original line number Diff line number Diff line
@@ -14,12 +14,12 @@
#pragma once
#include "element.h"

namespace fviz::elements::chart::legend {
namespace fviz::elements::legend {

ReturnCode build(
    const Environment& env,
    const Expr* expr,
    ElementRef* elem);

} // namespace fviz::elements::chart::legend
} // namespace fviz::elements::legend
Loading