Skip to content

Commit

Permalink
feat(components): add RichText component (#875)
Browse files Browse the repository at this point in the history
* Add RichText component

* Use more efficient traversal

* Fix traversal name

* Clarify naming

* Add DSL for RichText

* Create DSL module

* Include DSL module in RichText

* Remove DSLType

* Move smoothing to RichTextView

* Remove unnecessary font change

* Keep DSL in own module

* Revert "Keep DSL in own module"

This reverts commit 17e5445.

Co-authored-by: Glenn Slotte <glenn@slotte.net>
  • Loading branch information
ericluap and glennsl authored Jun 5, 2020
1 parent 55e5c4b commit 6255331
Show file tree
Hide file tree
Showing 7 changed files with 287 additions and 0 deletions.
5 changes: 5 additions & 0 deletions examples/Examples.re
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,11 @@ let examples = [
render: _ => FileDragAndDrop.render(),
source: "FileDragAndDrop.re",
},
{
name: "Rich Text Example",
render: _ => RichTextExample.render(),
source: "RichTextExample.re",
},
{
name: "Shell: Open URL",
render: _ => URLFileOpen.render(),
Expand Down
40 changes: 40 additions & 0 deletions examples/RichTextExample.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
open Revery;
open Revery.UI;
open Revery.UI.Components;
open Revery.Font;

let containerStyle =
Style.[
position(`Absolute),
top(0),
bottom(0),
left(0),
right(0),
alignItems(`Center),
justifyContent(`Center),
flexDirection(`Column),
];

module SampleRichText = {
let make = () => {
let richtext =
RichText.(
text("Hello ", ~color=Colors.red, ~fontWeight=Weight.Bold)
++ text("world", ~color=Colors.green)
++ text("!", ~color=Colors.yellow)
|> fontSize(20.)
|> italicized
);

let dimensions = RichText.measure(richtext);
let widthText = "Width: " ++ string_of_int(dimensions.width);
let heightText = "Height: " ++ string_of_int(dimensions.height);

<View style=containerStyle>
<RichTextView richtext />
<Text text=widthText />
<Text text=heightText />
</View>;
};
};
let render = () => <SampleRichText />;
1 change: 1 addition & 0 deletions src/UI/Revery_UI.re
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module LayoutTypes = Layout.LayoutTypes;
module Style = Style;
module Transform = Transform;
module Selector = Selector;
module RichText = RichText;

class node = class Node.node;
class viewNode = class ViewNode.viewNode;
Expand Down
129 changes: 129 additions & 0 deletions src/UI/RichText.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
open Revery_Core;
open Revery_Font;

type textInfo = {
fontFamily: Family.t,
fontWeight: Weight.t,
italicized: bool,
monospaced: bool,
fontSize: float,
text: string,
color: Color.t,
};
type t =
| Leaf(textInfo)
| Node(t, t);

let (++) = (left: t, right: t) => Node(left, right);

let rec foldRight =
(fn: ('acc, textInfo) => 'acc, accumulator: 'acc, richtext: t) =>
switch (richtext) {
| Leaf(textInfo) => fn(accumulator, textInfo)
| Node(left, right) =>
let rightAcc = foldRight(fn, accumulator, right);
let leftAcc = foldRight(fn, rightAcc, left);
leftAcc;
};

let rec map = (updateLeaf: textInfo => t, richtext: t) =>
switch (richtext) {
| Leaf(textInfo) => updateLeaf(textInfo)
| Node(left, right) =>
let newLeft = map(updateLeaf, left);
let newRight = map(updateLeaf, right);
Node(newLeft, newRight);
};

let measure = (~smoothing=Smoothing.default, richtext: t) =>
foldRight(
(acc: Dimensions.t, textInfo) => {
let dimensions =
Revery_Draw.Text.measure(
~smoothing,
~fontFamily=
Family.toPath(
textInfo.fontFamily,
textInfo.fontWeight,
textInfo.italicized,
textInfo.monospaced,
),
~fontSize=textInfo.fontSize,
textInfo.text,
);
let width = acc.width + int_of_float(dimensions.width);
let height = max(acc.height, int_of_float(dimensions.height));

Dimensions.create(~top=0, ~left=0, ~width, ~height, ());
},
Dimensions.create(~top=0, ~left=0, ~width=0, ~height=0, ()),
richtext,
);

module DSL = {
module Defaults = {
let fontFamily = Family.default;
let fontWeight = Weight.Normal;
let italicized = false;
let monospaced = false;
let fontSize = 14.;
let color = Colors.black;
};
let text =
(
~fontFamily=Defaults.fontFamily,
~fontWeight=Defaults.fontWeight,
~italicized=Defaults.italicized,
~monospaced=Defaults.monospaced,
~fontSize=Defaults.fontSize,
~color=Defaults.color,
text: string,
) =>
Leaf({
fontFamily,
fontWeight,
italicized,
monospaced,
fontSize,
text,
color,
});

let fontWeight = (fontWeight: Weight.t, richtext: t) =>
richtext |> map(textInfo => Leaf({...textInfo, fontWeight}));
let thin = (richtext: t) =>
richtext |> map(textInfo => Leaf({...textInfo, fontWeight: Weight.Thin}));
let ultralight = (richtext: t) =>
richtext
|> map(textInfo => Leaf({...textInfo, fontWeight: Weight.UltraLight}));
let light = (richtext: t) =>
richtext |> map(textInfo => Leaf({...textInfo, fontWeight: Weight.Light}));
let normal = (richtext: t) =>
richtext
|> map(textInfo => Leaf({...textInfo, fontWeight: Weight.Normal}));
let medium = (richtext: t) =>
richtext
|> map(textInfo => Leaf({...textInfo, fontWeight: Weight.Medium}));
let semibold = (richtext: t) =>
richtext
|> map(textInfo => Leaf({...textInfo, fontWeight: Weight.SemiBold}));
let bold = (richtext: t) =>
richtext |> map(textInfo => Leaf({...textInfo, fontWeight: Weight.Bold}));
let ultrabold = (richtext: t) =>
richtext
|> map(textInfo => Leaf({...textInfo, fontWeight: Weight.UltraBold}));
let heavy = (richtext: t) =>
richtext |> map(textInfo => Leaf({...textInfo, fontWeight: Weight.Heavy}));

let fontFamily = (fontFamily: Family.t, richtext: t) =>
richtext |> map(textInfo => Leaf({...textInfo, fontFamily}));
let italicized = (richtext: t) =>
richtext |> map(textInfo => Leaf({...textInfo, italicized: true}));
let monospaced = (richtext: t) =>
richtext |> map(textInfo => Leaf({...textInfo, monospaced: true}));
let fontSize = (fontSize: float, richtext: t) =>
richtext |> map(textInfo => Leaf({...textInfo, fontSize}));
let color = (color: Color.t, richtext: t) =>
richtext |> map(textInfo => Leaf({...textInfo, color}));
};
include DSL;
80 changes: 80 additions & 0 deletions src/UI/RichText.rei
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
open Revery_Core;
open Revery_Font;

type textInfo = {
fontFamily: Family.t,
fontWeight: Weight.t,
italicized: bool,
monospaced: bool,
fontSize: float,
text: string,
color: Color.t,
};
type t =
| Leaf(textInfo)
| Node(t, t);

let (++): (t, t) => t;

let foldRight: (('acc, textInfo) => 'acc, 'acc, t) => 'acc;

let map: (textInfo => t, t) => t;

let measure: (~smoothing: Smoothing.t=?, t) => Dimensions.t;

module DSL: {
let text:
(
~fontFamily: Family.t=?,
~fontWeight: Weight.t=?,
~italicized: bool=?,
~monospaced: bool=?,
~fontSize: float=?,
~color: Color.t=?,
string
) =>
t;
let fontWeight: (Weight.t, t) => t;
let thin: t => t;
let ultralight: t => t;
let light: t => t;
let normal: t => t;
let medium: t => t;
let semibold: t => t;
let bold: t => t;
let ultrabold: t => t;
let heavy: t => t;

let fontFamily: (Family.t, t) => t;
let italicized: t => t;
let monospaced: t => t;
let fontSize: (float, t) => t;
let color: (Color.t, t) => t;
};
let text:
(
~fontFamily: Family.t=?,
~fontWeight: Weight.t=?,
~italicized: bool=?,
~monospaced: bool=?,
~fontSize: float=?,
~color: Color.t=?,
string
) =>
t;
let fontWeight: (Weight.t, t) => t;
let thin: t => t;
let ultralight: t => t;
let light: t => t;
let normal: t => t;
let medium: t => t;
let semibold: t => t;
let bold: t => t;
let ultrabold: t => t;
let heavy: t => t;

let fontFamily: (Family.t, t) => t;
let italicized: t => t;
let monospaced: t => t;
let fontSize: (float, t) => t;
let color: (Color.t, t) => t;
1 change: 1 addition & 0 deletions src/UI_Components/Revery_UI_Components.re
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ module RadioButtonsString =
module ClickableText = ClickableText;
module Ticker = Ticker;
module Tree = Tree;
module RichTextView = RichTextView;
module Markdown = Markdown;
31 changes: 31 additions & 0 deletions src/UI_Components/RichTextView.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
open Revery_UI;
open Revery_UI_Primitives;
open Revery_Font;

let make =
(~style=[], ~smoothing=Smoothing.default, ~richtext: RichText.t, ()) => {
let text =
RichText.foldRight(
(acc, textInfo) =>
[
<Text
style=Style.[color(textInfo.color)]
fontFamily={textInfo.fontFamily}
fontWeight={textInfo.fontWeight}
italicized={textInfo.italicized}
monospaced={textInfo.monospaced}
fontSize={textInfo.fontSize}
text={textInfo.text}
smoothing
/>,
...acc,
],
[],
richtext,
);

// TODO: Add alignItems(`Baseline) if that exists
<View style=Style.[flexDirection(`Row), ...style]>
{text |> React.listToElement}
</View>;
};

0 comments on commit 6255331

Please sign in to comment.