evaluators) {
72 | super();
73 | if (num > 1)
74 | this.evaluators.add(new And(evaluators));
75 | else // 0 or 1
76 | this.evaluators.addAll(evaluators);
77 | updateNumEvaluators();
78 | }
79 |
80 | Or(Evaluator... evaluators) { this(Arrays.asList(evaluators)); }
81 |
82 | Or() {
83 | super();
84 | }
85 |
86 | public void add(Evaluator e) {
87 | evaluators.add(e);
88 | updateNumEvaluators();
89 | }
90 |
91 | @Override
92 | public boolean matches(Element root, Element node) {
93 | for (int i = 0; i < num; i++) {
94 | Evaluator s = evaluators.get(i);
95 | if (s.matches(root, node))
96 | return true;
97 | }
98 | return false;
99 | }
100 |
101 | @Override
102 | public String toString() {
103 | return StringUtil.join(evaluators, ", ");
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/html/src/org/jsoup/select/NodeFilter.java:
--------------------------------------------------------------------------------
1 | package org.jsoup.select;
2 |
3 | import org.jsoup.nodes.Node;
4 |
5 | /**
6 | * Node filter interface. Provide an implementing class to {@link NodeTraversor} to iterate through nodes.
7 | *
8 | * This interface provides two methods, {@code head} and {@code tail}. The head method is called when the node is first
9 | * seen, and the tail method when all of the node's children have been visited. As an example, head can be used to
10 | * create a start tag for a node, and tail to create the end tag.
11 | *
12 | *
13 | * For every node, the filter has to decide whether to
14 | *
15 | * - continue ({@link FilterResult#CONTINUE}),
16 | * - skip all children ({@link FilterResult#SKIP_CHILDREN}),
17 | * - skip node entirely ({@link FilterResult#SKIP_ENTIRELY}),
18 | * - remove the subtree ({@link FilterResult#REMOVE}),
19 | * - interrupt the iteration and return ({@link FilterResult#STOP}).
20 | *
21 | * The difference between {@link FilterResult#SKIP_CHILDREN} and {@link FilterResult#SKIP_ENTIRELY} is that the first
22 | * will invoke {@link NodeFilter#tail(Node, int)} on the node, while the latter will not.
23 | * Within {@link NodeFilter#tail(Node, int)}, both are equivalent to {@link FilterResult#CONTINUE}.
24 | *
25 | */
26 | public interface NodeFilter {
27 | /**
28 | * Filter decision.
29 | */
30 | enum FilterResult {
31 | /** Continue processing the tree */
32 | CONTINUE,
33 | /** Skip the child nodes, but do call {@link NodeFilter#tail(Node, int)} next. */
34 | SKIP_CHILDREN,
35 | /** Skip the subtree, and do not call {@link NodeFilter#tail(Node, int)}. */
36 | SKIP_ENTIRELY,
37 | /** Remove the node and its children */
38 | REMOVE,
39 | /** Stop processing */
40 | STOP
41 | }
42 |
43 | /**
44 | * Callback for when a node is first visited.
45 | * @param node the node being visited.
46 | * @param depth the depth of the node, relative to the root node. E.g., the root node has depth 0, and a child node of that will have depth 1.
47 | * @return Filter decision
48 | */
49 | FilterResult head(Node node, int depth);
50 |
51 | /**
52 | * Callback for when a node is last visited, after all of its descendants have been visited.
53 | * @param node the node being visited.
54 | * @param depth the depth of the node, relative to the root node. E.g., the root node has depth 0, and a child node of that will have depth 1.
55 | * @return Filter decision
56 | */
57 | FilterResult tail(Node node, int depth);
58 | }
59 |
--------------------------------------------------------------------------------
/html/src/org/jsoup/select/NodeTraversor.java:
--------------------------------------------------------------------------------
1 | package org.jsoup.select;
2 |
3 | import org.jsoup.helper.Validate;
4 | import org.jsoup.nodes.Element;
5 | import org.jsoup.nodes.Node;
6 | import org.jsoup.select.NodeFilter.FilterResult;
7 |
8 | /**
9 | * Depth-first node traversor. Use to iterate through all nodes under and including the specified root node.
10 | *
11 | * This implementation does not use recursion, so a deep DOM does not risk blowing the stack.
12 | *
13 | */
14 | public class NodeTraversor {
15 | private NodeVisitor visitor;
16 |
17 | /**
18 | * Create a new traversor.
19 | * @param visitor a class implementing the {@link NodeVisitor} interface, to be called when visiting each node.
20 | * @deprecated Just use the static {@link NodeTraversor#filter(NodeFilter, Node)} method.
21 | */
22 | public NodeTraversor(NodeVisitor visitor) {
23 | this.visitor = visitor;
24 | }
25 |
26 | /**
27 | * Start a depth-first traverse of the root and all of its descendants.
28 | * @param root the root node point to traverse.
29 | * @deprecated Just use the static {@link NodeTraversor#filter(NodeFilter, Node)} method.
30 | */
31 | public void traverse(Node root) {
32 | traverse(visitor, root);
33 | }
34 |
35 | /**
36 | * Start a depth-first traverse of the root and all of its descendants.
37 | * @param visitor Node visitor.
38 | * @param root the root node point to traverse.
39 | */
40 | public static void traverse(NodeVisitor visitor, Node root) {
41 | Node node = root;
42 | int depth = 0;
43 |
44 | while (node != null) {
45 | visitor.head(node, depth);
46 | if (node.childNodeSize() > 0) {
47 | node = node.childNode(0);
48 | depth++;
49 | } else {
50 | while (node.nextSibling() == null && depth > 0) {
51 | visitor.tail(node, depth);
52 | node = node.parentNode();
53 | depth--;
54 | }
55 | visitor.tail(node, depth);
56 | if (node == root)
57 | break;
58 | node = node.nextSibling();
59 | }
60 | }
61 | }
62 |
63 | /**
64 | * Start a depth-first traverse of all elements.
65 | * @param visitor Node visitor.
66 | * @param elements Elements to filter.
67 | */
68 | public static void traverse(NodeVisitor visitor, Elements elements) {
69 | Validate.notNull(visitor);
70 | Validate.notNull(elements);
71 | for (Element el : elements)
72 | traverse(visitor, el);
73 | }
74 |
75 | /**
76 | * Start a depth-first filtering of the root and all of its descendants.
77 | * @param filter Node visitor.
78 | * @param root the root node point to traverse.
79 | * @return The filter result of the root node, or {@link FilterResult#STOP}.
80 | */
81 | public static FilterResult filter(NodeFilter filter, Node root) {
82 | Node node = root;
83 | int depth = 0;
84 |
85 | while (node != null) {
86 | FilterResult result = filter.head(node, depth);
87 | if (result == FilterResult.STOP)
88 | return result;
89 | // Descend into child nodes:
90 | if (result == FilterResult.CONTINUE && node.childNodeSize() > 0) {
91 | node = node.childNode(0);
92 | ++depth;
93 | continue;
94 | }
95 | // No siblings, move upwards:
96 | while (node.nextSibling() == null && depth > 0) {
97 | // 'tail' current node:
98 | if (result == FilterResult.CONTINUE || result == FilterResult.SKIP_CHILDREN) {
99 | result = filter.tail(node, depth);
100 | if (result == FilterResult.STOP)
101 | return result;
102 | }
103 | Node prev = node; // In case we need to remove it below.
104 | node = node.parentNode();
105 | depth--;
106 | if (result == FilterResult.REMOVE)
107 | prev.remove(); // Remove AFTER finding parent.
108 | result = FilterResult.CONTINUE; // Parent was not pruned.
109 | }
110 | // 'tail' current node, then proceed with siblings:
111 | if (result == FilterResult.CONTINUE || result == FilterResult.SKIP_CHILDREN) {
112 | result = filter.tail(node, depth);
113 | if (result == FilterResult.STOP)
114 | return result;
115 | }
116 | if (node == root)
117 | return result;
118 | Node prev = node; // In case we need to remove it below.
119 | node = node.nextSibling();
120 | if (result == FilterResult.REMOVE)
121 | prev.remove(); // Remove AFTER finding sibling.
122 | }
123 | // root == null?
124 | return FilterResult.CONTINUE;
125 | }
126 |
127 | /**
128 | * Start a depth-first filtering of all elements.
129 | * @param filter Node filter.
130 | * @param elements Elements to filter.
131 | */
132 | public static void filter(NodeFilter filter, Elements elements) {
133 | Validate.notNull(filter);
134 | Validate.notNull(elements);
135 | for (Element el : elements)
136 | if (filter(filter, el) == FilterResult.STOP)
137 | break;
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/html/src/org/jsoup/select/NodeVisitor.java:
--------------------------------------------------------------------------------
1 | package org.jsoup.select;
2 |
3 | import org.jsoup.nodes.Node;
4 |
5 | /**
6 | * Node visitor interface. Provide an implementing class to {@link NodeTraversor} to iterate through nodes.
7 | *
8 | * This interface provides two methods, {@code head} and {@code tail}. The head method is called when the node is first
9 | * seen, and the tail method when all of the node's children have been visited. As an example, head can be used to
10 | * create a start tag for a node, and tail to create the end tag.
11 | *
12 | */
13 | public interface NodeVisitor {
14 | /**
15 | * Callback for when a node is first visited.
16 | *
17 | * @param node the node being visited.
18 | * @param depth the depth of the node, relative to the root node. E.g., the root node has depth 0, and a child node
19 | * of that will have depth 1.
20 | */
21 | void head(Node node, int depth);
22 |
23 | /**
24 | * Callback for when a node is last visited, after all of its descendants have been visited.
25 | *
26 | * @param node the node being visited.
27 | * @param depth the depth of the node, relative to the root node. E.g., the root node has depth 0, and a child node
28 | * of that will have depth 1.
29 | */
30 | void tail(Node node, int depth);
31 | }
32 |
--------------------------------------------------------------------------------
/html/src/org/jsoup/select/StructuralEvaluator.java:
--------------------------------------------------------------------------------
1 | package org.jsoup.select;
2 |
3 | import org.jsoup.nodes.Element;
4 |
5 | /**
6 | * Base structural evaluator.
7 | */
8 | abstract class StructuralEvaluator extends Evaluator {
9 | Evaluator evaluator;
10 |
11 | static class Root extends Evaluator {
12 | public boolean matches(Element root, Element element) {
13 | return root == element;
14 | }
15 | }
16 |
17 | static class Has extends StructuralEvaluator {
18 | public Has(Evaluator evaluator) {
19 | this.evaluator = evaluator;
20 | }
21 |
22 | public boolean matches(Element root, Element element) {
23 | for (Element e : element.getAllElements()) {
24 | if (e != element && evaluator.matches(root, e))
25 | return true;
26 | }
27 | return false;
28 | }
29 |
30 | @Override
31 | public String toString() {
32 | return String.format(":has(%s)", evaluator);
33 | }
34 | }
35 |
36 | static class Not extends StructuralEvaluator {
37 | public Not(Evaluator evaluator) {
38 | this.evaluator = evaluator;
39 | }
40 |
41 | public boolean matches(Element root, Element node) {
42 | return !evaluator.matches(root, node);
43 | }
44 |
45 | @Override
46 | public String toString() {
47 | return String.format(":not%s", evaluator);
48 | }
49 | }
50 |
51 | static class Parent extends StructuralEvaluator {
52 | public Parent(Evaluator evaluator) {
53 | this.evaluator = evaluator;
54 | }
55 |
56 | public boolean matches(Element root, Element element) {
57 | if (root == element)
58 | return false;
59 |
60 | Element parent = element.parent();
61 | while (true) {
62 | if (evaluator.matches(root, parent))
63 | return true;
64 | if (parent == root)
65 | break;
66 | parent = parent.parent();
67 | }
68 | return false;
69 | }
70 |
71 | @Override
72 | public String toString() {
73 | return String.format(":parent%s", evaluator);
74 | }
75 | }
76 |
77 | static class ImmediateParent extends StructuralEvaluator {
78 | public ImmediateParent(Evaluator evaluator) {
79 | this.evaluator = evaluator;
80 | }
81 |
82 | public boolean matches(Element root, Element element) {
83 | if (root == element)
84 | return false;
85 |
86 | Element parent = element.parent();
87 | return parent != null && evaluator.matches(root, parent);
88 | }
89 |
90 | @Override
91 | public String toString() {
92 | return String.format(":ImmediateParent%s", evaluator);
93 | }
94 | }
95 |
96 | static class PreviousSibling extends StructuralEvaluator {
97 | public PreviousSibling(Evaluator evaluator) {
98 | this.evaluator = evaluator;
99 | }
100 |
101 | public boolean matches(Element root, Element element) {
102 | if (root == element)
103 | return false;
104 |
105 | Element prev = element.previousElementSibling();
106 |
107 | while (prev != null) {
108 | if (evaluator.matches(root, prev))
109 | return true;
110 |
111 | prev = prev.previousElementSibling();
112 | }
113 | return false;
114 | }
115 |
116 | @Override
117 | public String toString() {
118 | return String.format(":prev*%s", evaluator);
119 | }
120 | }
121 |
122 | static class ImmediatePreviousSibling extends StructuralEvaluator {
123 | public ImmediatePreviousSibling(Evaluator evaluator) {
124 | this.evaluator = evaluator;
125 | }
126 |
127 | public boolean matches(Element root, Element element) {
128 | if (root == element)
129 | return false;
130 |
131 | Element prev = element.previousElementSibling();
132 | return prev != null && evaluator.matches(root, prev);
133 | }
134 |
135 | @Override
136 | public String toString() {
137 | return String.format(":prev%s", evaluator);
138 | }
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/html/src/org/jsoup/select/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | Packages to support the CSS-style element selector.
3 | */
4 | package org.jsoup.select;
--------------------------------------------------------------------------------
/html/src/team/rpsg/gdxQuery/Callback.java:
--------------------------------------------------------------------------------
1 | package team.rpsg.gdxQuery;
2 |
3 | public interface Callback {
4 | public T run();
5 | }
6 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/gdxQuery/CustomCallback.java:
--------------------------------------------------------------------------------
1 | package team.rpsg.gdxQuery;
2 |
3 | public interface CustomCallback {
4 | public T2 run(T t);
5 | }
6 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/gdxQuery/CustomRunnable.java:
--------------------------------------------------------------------------------
1 | package team.rpsg.gdxQuery;
2 |
3 | public interface CustomRunnable {
4 | public void run(T t);
5 | }
6 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/gdxQuery/Each.java:
--------------------------------------------------------------------------------
1 | package team.rpsg.gdxQuery;
2 |
3 | public interface Each {
4 | public void run(int index, T t);
5 | }
6 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/gdxQuery/GdxCellQuery.java:
--------------------------------------------------------------------------------
1 | package team.rpsg.gdxQuery;
2 |
3 | import com.badlogic.gdx.scenes.scene2d.Actor;
4 | import com.badlogic.gdx.scenes.scene2d.ui.Cell;
5 | /**
6 | * GDX-Query
7 | * more simplified way to enjoy LibGDX
8 | *
9 | * Project website: https://github.com/dingjibang/GDX-Query/
10 | * RPSG-TEAM: http://www.rpsg-team.com
11 | *
12 | * @author dingjibang
13 | *
14 | */
15 | public class GdxCellQuery{
16 |
17 | public static final Test FIRST_CHILD = cq -> cq.query.first().get() == cq.getActor();
18 | public static final Test LAST_CHILD = cq -> cq.query.last().get() == cq.getActor();
19 | public static Test INDEX(int i) {
20 | return cell -> cell.query.indexOf(cell.getActor()) == i;
21 | }
22 |
23 | GdxQuery listener;
24 | TQ query;
25 | Cell cell;
26 |
27 |
28 | public static GdxCellQuery build(TQ query, Cell cell){
29 | GdxCellQuery q = new GdxCellQuery<>();
30 | q.query = query;
31 | q.cell = cell;
32 | return q;
33 | }
34 |
35 | public GdxCellQuery size(float w, float h) {
36 | cell.size(w, h);
37 | return this;
38 | }
39 |
40 | public GdxCellQuery prefSize(float w, float h) {
41 | cell.prefSize(w, h);
42 | return this;
43 | }
44 |
45 | public GdxCellQuery left(){
46 | cell.left();
47 | return this;
48 | }
49 |
50 | public GdxCellQuery right(){
51 | cell.right();
52 | return this;
53 | }
54 |
55 | public GdxCellQuery top(){
56 | cell.top();
57 | return this;
58 | }
59 |
60 | public GdxCellQuery bottom(){
61 | cell.bottom();
62 | return this;
63 | }
64 |
65 | public TQ end(){
66 | return query;
67 | }
68 |
69 | public GdxCellQuery click(Runnable r){
70 | listener = new GdxQuery(cell.getActor()).click(r);
71 | return this;
72 | }
73 |
74 | public GdxCellQuery clickIf(Test t){
75 | if(t.test(this))
76 | click();
77 | return this;
78 | }
79 |
80 | public GdxCellQuery click(){
81 | if(listener != null)
82 | listener.click();
83 | return this;
84 | }
85 |
86 | public GdxCellQuery bind(Object o){
87 | cell.getActor().setUserObject(o);
88 | return this;
89 | }
90 |
91 | public Object object(){
92 | return cell.getActor().getUserObject();
93 | }
94 |
95 | @SuppressWarnings("unchecked")
96 | public GdxCellQuery cell(T2 a){
97 | return (GdxCellQuery)end().cell(a);
98 | }
99 |
100 | public GdxCellQuery padLeft(int i) {
101 | cell.padLeft(i);
102 | return this;
103 | }
104 |
105 | public GdxCellQuery padRight(int i) {
106 | cell.padRight(i);
107 | return this;
108 | }
109 |
110 | public GdxCellQuery padTop(int i) {
111 | cell.padTop(i);
112 | return this;
113 | }
114 |
115 | public GdxCellQuery padBottom(int i) {
116 | cell.padBottom(i);
117 | return this;
118 | }
119 |
120 | public GdxCellQuery padLR(int i) {
121 | return padLeft(i).padRight(i);
122 | }
123 |
124 | public GdxCellQuery padTB(int i) {
125 | return padTop(i).padBottom(i);
126 | }
127 |
128 | public GdxCellQuery width(int i) {
129 | cell.width(i);
130 | return this;
131 | }
132 |
133 | public GdxCellQuery height(int i) {
134 | cell.height(i);
135 | return this;
136 | }
137 |
138 | public GdxCellQuery pad(int i) {
139 | return padTB(i).padLR(i);
140 | }
141 |
142 | public GdxCellQuery center() {
143 | cell.center();
144 | return this;
145 | }
146 |
147 | public T getActor() {
148 | return cell.getActor();
149 | }
150 |
151 | public GdxCellQuery getActor(CustomRunnable run){
152 | run.run(getActor());
153 | return this;
154 | }
155 |
156 | public GdxCellQuery when(Test firstChild, CustomRunnable run) {
157 | if(firstChild.test(this))
158 | run.run(getActor());
159 | return this;
160 | }
161 |
162 | public GdxCellQuery row() {
163 | query.row();
164 | return this;
165 | }
166 |
167 | }
--------------------------------------------------------------------------------
/html/src/team/rpsg/gdxQuery/GdxFrame.java:
--------------------------------------------------------------------------------
1 | package team.rpsg.gdxQuery;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | public class GdxFrame {
7 | private Map frames = new HashMap();
8 |
9 | public GdxFrame add(GdxQuery query,GdxQueryRunnable runnable){
10 | frames.put(query,runnable);
11 | return this;
12 | }
13 |
14 | public GdxFrame logic(){
15 | for(GdxQuery query:frames.keySet())
16 | frames.get(query).run(query);
17 | return this;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/gdxQuery/GdxQueryRunnable.java:
--------------------------------------------------------------------------------
1 | package team.rpsg.gdxQuery;
2 |
3 | public interface GdxQueryRunnable {
4 | public void run(GdxQuery self);
5 | }
6 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/gdxQuery/ListQuery.java:
--------------------------------------------------------------------------------
1 | package team.rpsg.gdxQuery;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public class ListQuery{
7 |
8 | private List list = new ArrayList<>();
9 |
10 | public ListQuery(T1 t1) {
11 | add(t1);
12 | }
13 |
14 | public ListQuery(Class t1) {
15 |
16 | }
17 |
18 | public ListQuery() {
19 | }
20 |
21 | public ListQuery add(T1 t1){
22 | list.add(t1);
23 | return this;
24 | }
25 |
26 | public List get(){
27 | return list;
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/gdxQuery/Map.java:
--------------------------------------------------------------------------------
1 | package team.rpsg.gdxQuery;
2 |
3 | public interface Map{
4 | public R run(T t);
5 | }
6 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/gdxQuery/MapQuery.java:
--------------------------------------------------------------------------------
1 | package team.rpsg.gdxQuery;
2 |
3 | import java.util.HashMap;
4 |
5 | public class MapQuery extends HashMap{
6 | private static final long serialVersionUID = 1L;
7 |
8 | public MapQuery(T1 t1, T2 t2) {
9 | add(t1,t2);
10 | }
11 |
12 | public MapQuery(Class t1, Class t2) {
13 |
14 | }
15 |
16 | public MapQuery() {
17 | }
18 |
19 | public MapQuery add(T1 t1,T2 t2){
20 | put(t1,t2);
21 | return this;
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/gdxQuery/NullActor.java:
--------------------------------------------------------------------------------
1 | package team.rpsg.gdxQuery;
2 |
3 | import com.badlogic.gdx.scenes.scene2d.Actor;
4 |
5 | public class NullActor extends Actor {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/gdxQuery/RemoveTest.java:
--------------------------------------------------------------------------------
1 | package team.rpsg.gdxQuery;
2 |
3 | public interface RemoveTest {
4 | public boolean test(T object);
5 | }
--------------------------------------------------------------------------------
/html/src/team/rpsg/gdxQuery/Test.java:
--------------------------------------------------------------------------------
1 | package team.rpsg.gdxQuery;
2 |
3 | public interface Test {
4 | public boolean test(GdxCellQuery, ? extends GdxQuery> cq);
5 | }
6 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/HTMLStage.groovy:
--------------------------------------------------------------------------------
1 | package team.rpsg.html
2 |
3 | import com.badlogic.gdx.Gdx
4 | import com.badlogic.gdx.graphics.OrthographicCamera
5 | import com.badlogic.gdx.scenes.scene2d.Stage
6 | import com.badlogic.gdx.scenes.scene2d.ui.Container
7 | import com.badlogic.gdx.scenes.scene2d.utils.Drawable
8 | import com.badlogic.gdx.utils.Scaling
9 | import com.badlogic.gdx.utils.viewport.ScalingViewport
10 | import com.steadystate.css.dom.CSSRuleListImpl
11 | import com.steadystate.css.dom.CSSStyleDeclarationImpl
12 | import com.steadystate.css.dom.CSSStyleRuleImpl
13 | import com.steadystate.css.dom.CSSUnknownRuleImpl
14 | import com.steadystate.css.parser.CSSOMParser
15 | import com.steadystate.css.parser.SACParserCSS3
16 | import com.sun.webkit.dom.DocumentImpl
17 | import groovy.transform.CompileStatic
18 | import org.jsoup.Jsoup
19 | import org.jsoup.nodes.Document
20 | import org.w3c.css.sac.InputSource
21 | import org.w3c.css.sac.Selector
22 | import org.w3c.dom.css.CSSStyleSheet
23 | import team.rpsg.html.dom.Root
24 | import team.rpsg.html.dom.Dom
25 | import team.rpsg.html.manager.ResourceManager
26 | import team.rpsg.html.util.PathParser
27 | import team.rpsg.html.util.Timer
28 |
29 | import javax.xml.transform.dom.DOMSource
30 |
31 | /**
32 | * GDX-HTML
33 | * using HTMLStage.buildPath(path-to-file) to create your first stage.
34 | */
35 |
36 |
37 | @CompileStatic
38 | class HTMLStage extends Stage{
39 |
40 | Timer timer
41 | Document document
42 | Dom rootDom
43 | ResourceManager res
44 |
45 | boolean needsRefresh = false
46 |
47 | List styles = []
48 |
49 | private HTMLStage(Document document) {
50 | super(new ScalingViewport(Scaling.none, Gdx.graphics.width, Gdx.graphics.height, new OrthographicCamera()))
51 |
52 | this.res = new ResourceManager()
53 | this.document = document
54 |
55 | def base = document.getElementsByTag("base")
56 | if(base.size() != 0){
57 | this.document.baseUri = base.last().attr("href")
58 | }
59 |
60 |
61 | forceBuild()
62 | }
63 |
64 | void forceBuild(){
65 | timer = new Timer()
66 | this.clear()
67 | styles.clear()
68 |
69 | document.getElementsByTag("style").each {styles << it.childNode(0).toString()}
70 | document.select("link[rel=\"stylesheet\"]").each {
71 | PathParser.get(document, it.attr("href"), it.attr("charset"), {String s, boolean needsUpdate ->
72 | styles << s
73 | if(needsUpdate)
74 | joinStyles(s)
75 | })
76 | }
77 |
78 |
79 | joinStyles()
80 | }
81 |
82 | void refresh(boolean isHardRefresh = false){
83 | if(isHardRefresh){
84 | forceBuild()
85 | }else{
86 | clear()
87 | addActor(rootDom = new Root(document.body(), this))
88 | }
89 |
90 | debugAll = document.getElementsByTag("html").attr("debug").equalsIgnoreCase("true")
91 | }
92 |
93 |
94 | void joinStyles(String style = null){
95 | InputSource source = new InputSource(new StringReader(style ?: styles.join("\n")))
96 | CSSOMParser parser = new CSSOMParser(new SACParserCSS3())
97 | CSSStyleSheet styleSheet = parser.parseStyleSheet(source, null, null)
98 |
99 | CSSRuleListImpl rules = styleSheet.getCssRules() as CSSRuleListImpl
100 | for(def i = 0; i < rules.getLength(); i++){
101 | if(!(rules.item(i) instanceof CSSStyleRuleImpl))
102 | continue
103 |
104 | CSSStyleRuleImpl item = rules.item(i) as CSSStyleRuleImpl
105 |
106 | for(def j = 0; j < item.selectors.getLength(); j++) {
107 | Selector selector = item.selectors.item(j)
108 |
109 | def s = selector.toString().trim()
110 |
111 | if(s.indexOf(':') >= 0)
112 | continue
113 |
114 | if(s.length() == 0)
115 | continue
116 |
117 | //println "s: " + s + ", default: " + selector.toString()
118 |
119 | try{
120 | document.select(s)*.styles*.add ((item.style as CSSStyleDeclarationImpl).properties)
121 | }catch(e){
122 | e.printStackTrace()
123 | }
124 |
125 | }
126 | }
127 |
128 | needsRefresh = true
129 | }
130 |
131 | void draw() {
132 | if(needsRefresh){
133 | needsRefresh = false
134 | refresh()
135 | }
136 | super.draw()
137 | }
138 |
139 | void act() {
140 | timer.act()
141 | res.act()
142 | super.act()
143 | }
144 |
145 | static HTMLStage buildHTML(String html, String uriPath = null){
146 | def doc = Jsoup.parse(html)
147 |
148 | if(uriPath)
149 | doc.baseUri = uriPath
150 |
151 | buildDocument doc
152 | }
153 |
154 | static HTMLStage buildDocument(Document document){
155 | new HTMLStage(document)
156 | }
157 |
158 | static HTMLStage buildPath(String path){
159 | buildHTML(Gdx.files.internal(path).readString("utf-8"), path)
160 | }
161 |
162 | void dispose() {
163 | document = null
164 | rootDom = null
165 | res.dispose()
166 | super.dispose()
167 | }
168 |
169 | void resize(int width, int height){
170 | viewport.setWorldSize(width, height)
171 | viewport.update(width, height, true)
172 |
173 | refresh(false)
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/dom/HTMLDom.groovy:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.dom
2 |
3 | import com.steadystate.css.dom.Property
4 | import groovy.transform.CompileStatic
5 | import org.jsoup.nodes.Node
6 |
7 | @CompileStatic
8 | class HTMLDom extends Dom {
9 | private String tagName
10 |
11 | HTMLDom(Node node, List styles, String tagName){
12 | super(node)
13 | this.node.defaultStyles = styles
14 | this.tagName = tagName
15 | }
16 |
17 | String toString() {
18 | return "HTMLDom(${tagName}) ${super.toString()}"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/dom/HTMLDomPreset.groovy:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.dom
2 |
3 | import com.steadystate.css.dom.CSSStyleDeclarationImpl
4 | import com.steadystate.css.dom.Property
5 | import com.steadystate.css.parser.CSSOMParser
6 | import com.steadystate.css.parser.SACParserCSS3
7 | import groovy.transform.CompileStatic
8 | import org.w3c.css.sac.InputSource
9 | import team.rpsg.html.util.StyleParser
10 |
11 | @CompileStatic
12 | enum HTMLDomPreset {
13 | ADDRESS("display: block;font-style: italic;"),
14 | AREA("display: none;"),
15 | ARTICLE("display: block; "),
16 | ASIDE("display: block; "),
17 | B("font-weight: bold;"),
18 | BDO("unicode-bidi: bidi-override;"),
19 | BLOCKQUOTE("display: block;margin-top: 1em;margin-bottom: 1em;margin-left: 40px;margin-right: 40px;"),
20 | BODY("display: block;margin: 8px;"),
21 | BR("display: block;"),
22 | CAPTION("display: table-caption;text-align: center;"),
23 | CITE("font-style: italic;"),
24 | CODE("font-family: monospace;"),
25 | COL("display: table-column;"),
26 | COLGROUP("display: table-column-group"),
27 | DATALIST("display: none;"),
28 | DD("display: block;margin-left: 40px;"),
29 | DEL("text-decoration: line-through;"),
30 | DETAILS("display: block;"),
31 | DFN("font-style: italic;"),
32 | DIV("display: block;"),
33 | DL("display: block;margin-top: 1em;margin-bottom: 1em;margin-left: 0;margin-right: 0;"),
34 | DT("display: block;"),
35 | EM("font-style: italic;"),
36 | FIELDSET("display: block;margin-left: 2px;margin-right: 2px;padding-top: 0.35em;padding-bottom: 0.625em;padding-left: 0.75em;padding-right: 0.75em;border: 2px groove (internal value);"),
37 | FIGCAPTION("display: block;"),
38 | FIGURE("display: block;margin-top: 1em;margin-bottom: 1em;margin-left: 40px;margin-right: 40px;"),
39 | FOOTER("display: block;"),
40 | FORM("display: block;margin-top: 0em;"),
41 | H1("display: block;font-size: 2em;margin-top: 0.67em;margin-bottom: 0.67em;margin-left: 0;margin-right: 0;font-weight: bold;"),
42 | H2("display: block;font-size: 1.5em;margin-top: 0.83em;margin-bottom: 0.83em;margin-left: 0;margin-right: 0;font-weight: bold;"),
43 | H3("display: block;font-size: 1.17em;margin-top: 1em;margin-bottom: 1em;margin-left: 0;margin-right: 0;font-weight: bold;"),
44 | H4("display: block;margin-top: 1.33em;margin-bottom: 1.33em;margin-left: 0;margin-right: 0;font-weight: bold;"),
45 | H5("display: block;font-size: .83em;margin-top: 1.67em;margin-bottom: 1.67em;margin-left: 0;margin-right: 0;font-weight: bold;"),
46 | H6("display: block;font-size: .67em;margin-top: 2.33em;margin-bottom: 2.33em;margin-left: 0;margin-right: 0;font-weight: bold;"),
47 | HEAD("display: none;"),
48 | HEADER("display: block;"),
49 | HR("display: block;margin-top: 0.5em;margin-bottom: 0.5em;margin-left: auto;margin-right: auto;border-style: inset;border-width: 1px;"),
50 | HTML("display: block;"),
51 | I("font-style: italic;"),
52 | IMG("display: inline-block;"),
53 | IFRAME("display: block;"),
54 | INS("text-decoration: underline;"),
55 | KBD("font-family: monospace;"),
56 | LABEL("cursor: default;"),
57 | LEGEND("display: block;padding-left: 2px;padding-right: 2px;border: none;"),
58 | LI("display: list-item;"),
59 | LINK("display: none;"),
60 | MAP("display: inline;"),
61 | MARK("background-color: yellow;color: black;"),
62 | MENU("display: block;list-style-type: disc;margin-top: 1em;margin-bottom: 1em;margin-left: 0;margin-right: 0;padding-left: 40px;"),
63 | NAV("display: block;"),
64 | OL("display: block;list-style-type: decimal;margin-top: 1em;margin-bottom: 1em;margin-left: 0;margin-right: 0;padding-left: 40px;"),
65 | OUTPUT("display: inline;"),
66 | P("display: block;margin-top: 1em;margin-bottom: 1em;margin-left: 0;margin-right: 0;"),
67 | PARAM("display: none;"),
68 | PRE("display: block;font-family: monospace;white-space: pre;margin: 1em 0;"),
69 | Q("display: inline;"),
70 | RT("line-height: normal;"),
71 | S("text-decoration: line-through;"),
72 | SAMP("font-family: monospace;"),
73 | SCRIPT("display: none;"),
74 | SECTION("display: block;"),
75 | SMALL("font-size: smaller;"),
76 | STRIKE("text-decoration: line-through;"),
77 | STRONG("font-weight: bold;"),
78 | STYLE("display: none;"),
79 | SUB("vertical-align: sub;font-size: smaller;"),
80 | SUMMARY("display: block;"),
81 | SUP("vertical-align: super;font-size: smaller;"),
82 | TABLE("display: table;border-collapse: separate;border-spacing: 2px;border-color: gray;"),
83 | TBODY("display: table-row-group;vertical-align: middle;border-color: inherit;"),
84 | TD("display: table-cell;vertical-align: inherit;"),
85 | TFOOT("display: table-footer-group;vertical-align: middle;border-color: inherit;"),
86 | TH("display: table-cell;vertical-align: inherit;font-weight: bold;text-align: center;"),
87 | THEAD("display: table-header-group;vertical-align: middle;border-color: inherit;"),
88 | TITLE("display: none;"),
89 | TR("display: table-row;vertical-align: inherit;border-color: inherit;"),
90 | U("text-decoration: underline;"),
91 | UL("display: block;list-style-type: disc;margin-top: 1em;margin-bottom: 1 em;margin-left: 0;margin-right: 0;padding-left: 40px;"),
92 | VAR("font-style: italic;")
93 |
94 | private List styles
95 |
96 | private HTMLDomPreset(String stylesString){
97 | if(stylesString.length() == 0){
98 | styles = new ArrayList<>()
99 | return
100 | }
101 |
102 | styles = StyleParser.parse(stylesString).properties
103 | }
104 |
105 | HTMLDom dom(org.jsoup.nodes.Node node){
106 | return new HTMLDom(node, styles, name())
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/dom/Root.groovy:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.dom
2 |
3 | import com.badlogic.gdx.graphics.g2d.Batch
4 | import com.badlogic.gdx.utils.Align
5 | import groovy.transform.CompileStatic
6 | import org.jsoup.nodes.Node
7 | import team.rpsg.html.HTMLStage
8 |
9 | /**
10 | * rootDom = html document
11 | */
12 | @CompileStatic
13 | class Root extends Dom{
14 |
15 | Root(Node node, HTMLStage stage){
16 | super(node)
17 |
18 | this.stage = stage
19 |
20 | parse()
21 |
22 | width = stage.width
23 | height = stage.height
24 |
25 | build()
26 |
27 | x = 0
28 | y = 0
29 |
30 | buildChild()
31 |
32 | align(Align.topLeft)
33 | }
34 |
35 | void draw(Batch batch, float parentAlpha) {
36 | backgroundDrawable?.draw(batch, x, y, width, height)
37 | super.draw(batch, parentAlpha)
38 | }
39 |
40 | void dispose() {
41 |
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/dom/UnknownDom.groovy:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.dom
2 |
3 | import groovy.transform.CompileStatic
4 | import org.jsoup.nodes.Node
5 | @CompileStatic
6 | class UnknownDom extends Dom{
7 |
8 | UnknownDom(Node node){
9 | super(node)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/dom/layout/AbstractLayout.groovy:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.dom.layout
2 |
3 | import com.badlogic.gdx.scenes.scene2d.Actor
4 | import groovy.transform.CompileStatic
5 | import team.rpsg.html.dom.Dom
6 |
7 | @CompileStatic
8 | abstract class AbstractLayout {
9 | Dom dom
10 |
11 | AbstractLayout(Dom dom){
12 | this.dom = dom
13 | }
14 |
15 | abstract void parse();
16 | abstract Actor build();
17 |
18 | void addQuirks(Dom dom){
19 |
20 | }
21 |
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/dom/layout/InlineLayout.groovy:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.dom.layout
2 |
3 | import com.badlogic.gdx.scenes.scene2d.Actor
4 | import team.rpsg.html.dom.Dom
5 |
6 | class InlineLayout extends AbstractLayout {
7 | InlineLayout(Dom dom) {
8 | super(dom)
9 | }
10 |
11 | void parse() {
12 |
13 | }
14 |
15 | Actor build() {
16 | return null
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/dom/layout/TableLayout.groovy:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.dom.layout
2 |
3 | import com.badlogic.gdx.scenes.scene2d.Actor
4 | import com.badlogic.gdx.scenes.scene2d.ui.Cell
5 | import com.badlogic.gdx.scenes.scene2d.ui.Container
6 | import com.badlogic.gdx.scenes.scene2d.ui.Table
7 | import com.badlogic.gdx.scenes.scene2d.ui.Value
8 | import com.badlogic.gdx.utils.GdxRuntimeException
9 | import org.jsoup.nodes.Element
10 | import team.rpsg.html.dom.Dom
11 | import team.rpsg.html.util.AlignParser
12 | import team.rpsg.html.util.SizeParser
13 |
14 | class TableLayout extends AbstractLayout{
15 | static String[] DISPLAY_NAME = ["table", "table-row", "table-cell", "table-row-group", "table-header-group"]
16 |
17 | TableLayout parent
18 |
19 | boolean isTable, isTR, isTD, isTHead, isTBody
20 |
21 |
22 | Table table
23 | Cell current
24 |
25 | TableLayout(Dom dom) {
26 | super(dom)
27 | if(!(dom.node instanceof Element))
28 | throw new GdxRuntimeException("must case **DOM ELEMENT** to table.")
29 | }
30 |
31 | void parse(){
32 | if(dom.parentDom.layout instanceof TableLayout)
33 | parent = dom.parentDom.layout as TableLayout
34 |
35 | def display = dom.display
36 |
37 | switch (display.toLowerCase()){
38 | case "table":
39 | isTable = true
40 | break
41 | case "table-row":
42 | isTR = true
43 | break
44 | case "table-row-group":
45 | isTBody = true
46 | break
47 | case "table-header-group":
48 | isTHead = true
49 | break
50 | case "table-cell":
51 | isTD = true
52 | break
53 | }
54 | }
55 |
56 | Actor build() {
57 | table = parent?.table
58 |
59 | if(!isTable && table == null)
60 | return dom
61 |
62 | if(isTable){
63 |
64 | dom.parse()
65 |
66 | table = new Table().left().top()
67 | table.fillParent = true
68 |
69 | dom.debugStrings.add { "table:${Math.random()}" + table.width + ", " + table.height }
70 |
71 | def container = new Container(table)
72 | container.width(new Value() {
73 | float get(Actor context) {
74 | dom.widthValue != null ? dom.width : Math.max(context.width, (context as Table).prefWidth)
75 | }
76 | })
77 | container.height(new Value() {
78 | float get(Actor context) {
79 | dom.heightValue != null ? dom.height : Math.max(context.height, (context as Table).prefHeight)
80 | }
81 | })
82 | dom.addActor(container)
83 |
84 | return dom
85 |
86 | } else if(isTR || isTBody || isTHead){
87 | table.row()
88 | return null
89 | } else if(isTD){
90 | dom.parse()
91 | def c = add(dom)
92 | dom.debugStrings.add { "cell: pw" + c.getPrefWidth() + ", ph" + c.getPrefHeight() }
93 |
94 | return null
95 | }
96 |
97 | return dom
98 | }
99 |
100 | static def first = true
101 |
102 | private void setCellWidth(){
103 | current.width(new Value() {
104 | float get(Actor context) {
105 | Math.max((context as Dom).prefWidth, context.width)
106 | }
107 | })
108 | }
109 |
110 | private void setCellHeight(){
111 | current.height(new Value() {
112 | float get(Actor context) {
113 | Math.max((context as Dom).prefHeight, context.height)
114 | }
115 | })
116 | }
117 |
118 |
119 | Cell add(Dom dom){
120 | current = table.add(dom).align(AlignParser.join(dom.textAlign, dom.verticalAlign))
121 |
122 | if(dom.widthValue){
123 | if(dom.widthValue instanceof Value.Fixed){
124 | setCellWidth()
125 | }
126 |
127 | if(dom.widthValue instanceof SizeParser.percentInnerValue){
128 | def percent = (dom.widthValue as SizeParser.percentInnerValue).percent
129 | current.width(new Value() {
130 | float get(Actor context) {
131 | parent.table.width * percent
132 | }
133 | })
134 | }
135 | }else {
136 | setCellWidth()
137 | current.expandX()
138 | }
139 | if(dom.heightValue){
140 | if(dom.heightValue instanceof Value.Fixed){
141 | setCellHeight()
142 | }
143 | if(dom.heightValue instanceof SizeParser.percentInnerValue){
144 | def percent = (dom.heightValue as SizeParser.percentInnerValue).percent
145 | current.height(new Value() {
146 | float get(Actor context) {
147 | parent.table.height * percent
148 | }
149 | })
150 | }
151 | }else{
152 | setCellHeight()
153 | current.expandY()
154 | }
155 | }
156 |
157 | void addQuirks(Dom dom) {
158 |
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/dom/node/Image.groovy:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.dom.node
2 |
3 | import com.badlogic.gdx.graphics.g2d.Batch
4 | import com.badlogic.gdx.scenes.scene2d.Actor
5 | import com.badlogic.gdx.scenes.scene2d.ui.Container
6 | import com.badlogic.gdx.scenes.scene2d.ui.Value
7 | import com.badlogic.gdx.utils.Scaling
8 | import groovy.transform.CompileStatic
9 | import org.jsoup.nodes.Node
10 | import team.rpsg.html.HTMLStage
11 | import team.rpsg.html.dom.HTMLDom
12 | import team.rpsg.html.manager.widget.AsyncLoadImage
13 | import team.rpsg.html.util.PathParser
14 | import team.rpsg.html.util.StyleParser
15 |
16 | /**
17 | * HTML Image Node.
18 | * It uses lazy loading to load images asynchronously.
19 | *
20 | *
21 | * HTML的图片实现
22 | * 它是异步加载的哦。
23 | */
24 | @CompileStatic
25 | class Image extends HTMLDom {
26 |
27 | float size
28 |
29 | private static final String DEFAULT_STYLE = "display: inline-block;"
30 | String src = ""
31 | Scaling scaling
32 |
33 | team.rpsg.html.manager.widget.Image img
34 |
35 | boolean async = true
36 |
37 |
38 | Image(Node node) {
39 | super(node, StyleParser.parse(DEFAULT_STYLE).properties, "img")
40 | }
41 |
42 | void parse() {
43 | super.parse()
44 |
45 | src = node.attr "src"
46 | if(src){
47 | src = PathParser.parse((stage as HTMLStage).document, src)
48 | }
49 |
50 | scaling = style("-gdx-image-scaling", null, {str ->
51 | if(!str)
52 | return null
53 | return Scaling.valueOf(str.toString())
54 | }) as Scaling
55 |
56 | if(node.attr("async"))
57 | async = node.attr("async").equalsIgnoreCase("true")
58 | }
59 |
60 | void build() {
61 | super.build()
62 | if(src && src.length() != 0){
63 |
64 | if(async){
65 | boolean needH = false
66 | boolean needW = false
67 |
68 | Container container = new Container<>()
69 |
70 | def callback = {AsyncLoadImage img ->
71 | img.layout()
72 |
73 | if(needH){
74 | container.height(new Value() {
75 | float get(Actor context) {
76 | img.imageHeight
77 | }
78 | })
79 | }
80 |
81 | if(needW){
82 | container.width(new Value() {
83 | float get(Actor context) {
84 | img.imageWidth
85 | }
86 | })
87 | }
88 |
89 | }
90 |
91 | img = new AsyncLoadImage(src, callback, res)
92 | container = new Container(img)
93 |
94 |
95 | def setScaling = true
96 |
97 | if(width != 0 && height == 0){
98 | container.width(img.width = width)
99 | needH = true
100 | }else if(height != 0 && width == 0){
101 | container.height(img.height = height)
102 | needW = true
103 | }else if(width != 0 && height != 0){
104 | container.width(img.width = width)
105 | container.height(img.height = height)
106 | img.scaling = Scaling.stretch
107 | setScaling = false
108 | }
109 |
110 | if(setScaling || (!setScaling && scaling != null))
111 | img.scaling = scaling ?: Scaling.fit
112 |
113 |
114 |
115 | current.addActor container
116 | }else{
117 | img = new team.rpsg.html.manager.widget.Image(res.getTexture(src))
118 | if(width != 0 && height != 0){
119 | img.width = width
120 | img.height = height
121 | } else if(width != 0 && height == 0){
122 | img.width = width
123 | } else if(height != 0 && width == 0){
124 | img.height = height
125 | }
126 |
127 | img.scaling = scaling ?: Scaling.fit
128 | img.layout()
129 |
130 | width = img.imageWidth
131 | height = img.imageHeight
132 |
133 |
134 | current.addActor new Container(img)
135 | .width(new Value.Fixed(img.imageWidth))
136 | .height(new Value.Fixed(img.imageHeight))
137 |
138 | }
139 |
140 | }
141 | }
142 |
143 |
144 | void draw(Batch batch, float parentAlpha) {
145 | super.draw(batch, parentAlpha)
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/dom/node/Text.groovy:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.dom.node
2 |
3 | import com.badlogic.gdx.graphics.Color
4 | import com.badlogic.gdx.scenes.scene2d.ui.Container
5 | import com.badlogic.gdx.utils.Align
6 | import groovy.transform.CompileStatic
7 | import org.jsoup.nodes.Node
8 | import org.jsoup.nodes.TextNode
9 | import team.rpsg.html.dom.Dom
10 | import team.rpsg.html.manager.widget.Label
11 | import team.rpsg.html.util.AlignParser
12 | import team.rpsg.html.util.ColorParser
13 | import team.rpsg.html.util.SizeParser
14 |
15 | /**
16 | * HTML Text Node.
17 | * It is implemented by the label of LibGDX.
18 | * It defaults to not enabling wrap and can be enabled with "-gdx-wrap: true;"
19 | * To enabling wrap requires the parent container to set the width, but if the container contains multiple child elements, it will cause a width bug.
20 | * If you need to use multiple colored child elements in the wrap-enabled container, it is recommended to use "-gdx-markup: true; "
21 | *
22 | *
23 | * HTML文字Node
24 | * 它使用gdx的Label组件实现的,默认情况下它不会开启换行,你可以在它的Dom设置"-gdx-wrap: true;"
25 | * 但是,开启换行会导致一个宽度的bug(所有子元素宽度一致),目前没有办法解决除非重写一个文档流。
26 | * 所以如果想启用换行,还想让该段文字是多彩的,我们推荐你使用"-gdx-markup: true;",启用gdx的markup颜色语言,而不是使用一堆子元素。
27 | */
28 | @CompileStatic
29 | class Text extends Dom {
30 |
31 | int fontSize = 30
32 | def text = ""
33 | Color textColor = Color.WHITE
34 | Float lineHeight = Label.AUTO_LINE_HEIGHT
35 | Integer letterSpacing = Label.AUTO_LETTER_SPACING
36 |
37 | private String wrap
38 | private boolean markup
39 |
40 |
41 | Text(Node node) {
42 | super(node)
43 | }
44 |
45 | void parse() {
46 | super.parse()
47 |
48 | text = (node as TextNode).text()
49 | textColor = style("color", "white", ColorParser.&parse) as Color
50 | fontSize = style("font-size", "16px", SizeParser.&parseFontPX) as int
51 |
52 | wrap = style("-gdx-wrap", "false", {p -> p?.toString()})
53 | markup = style("-gdx-markup", "false", {p -> p?.toString()?.toBoolean()})
54 |
55 | lineHeight = style("line-height", null, {v -> v ? SizeParser.parse(v)?.get(parentDom)?.toFloat() : Label.AUTO_LINE_HEIGHT}) as Float
56 | letterSpacing = style("letter-spacing", null, {v -> v ? SizeParser.parse(v)?.get(parentDom)?.toInteger() : Label.AUTO_LETTER_SPACING}) as Integer
57 | }
58 |
59 | void build() {
60 | super.build()
61 | def label = res.text.getLabel(text, fontSize)
62 | label.color = textColor ?: Color.WHITE
63 | label.lineHeight = lineHeight
64 | label.letterSpacing = letterSpacing
65 |
66 | label.alignment = Align.left
67 |
68 | if(markup)
69 | label.style.font.data.markupEnabled = markup
70 |
71 | if(wrap && wrap == "true"){
72 | label.wrap = true
73 |
74 | def container = new Container(label).align(textAlign)
75 | container.width(parentDom.width)
76 | current.addActor container
77 | }else {
78 | current.addActor label
79 | }
80 |
81 |
82 |
83 |
84 |
85 | }
86 |
87 | void dispose() {
88 |
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/manager/ResourceManager.groovy:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.manager
2 |
3 | import com.badlogic.gdx.Gdx
4 | import com.badlogic.gdx.assets.AssetLoaderParameters
5 | import com.badlogic.gdx.assets.AssetManager
6 | import com.badlogic.gdx.graphics.Color
7 | import com.badlogic.gdx.graphics.Pixmap
8 | import com.badlogic.gdx.graphics.Texture
9 | import com.badlogic.gdx.graphics.g2d.Sprite
10 | import com.badlogic.gdx.graphics.g2d.TextureRegion
11 | import com.badlogic.gdx.scenes.scene2d.utils.Drawable
12 | import com.badlogic.gdx.scenes.scene2d.utils.SpriteDrawable
13 | import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable
14 | import groovy.transform.CompileStatic
15 | import team.rpsg.html.manager.widget.AsyncLoadImage
16 | import team.rpsg.html.manager.widget.Image
17 |
18 | @CompileStatic
19 | class ResourceManager {
20 |
21 | /**资源管理线程*/
22 | public AssetManager assetManager
23 | /**字体管理*/
24 | public TextManager text
25 |
26 | private SpriteDrawable defaultSpriteDrawable
27 |
28 | /**初始化*/
29 | ResourceManager(){
30 | assetManager = new AssetManager()
31 | /**添加字体管理器*/
32 | text = new TextManager()
33 |
34 | def pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888)
35 | pixmap.drawPixel(0, 0, Color.rgba8888(Color.WHITE))
36 |
37 | def tex = new Texture(pixmap)
38 | pixmap.dispose()
39 |
40 | defaultSpriteDrawable = new SpriteDrawable(new Sprite(tex))
41 | }
42 |
43 | /**
44 | * 获取一张图片,它是异步加载的,如果需要同步加载,请调用{@link #sync(String) sync}方法
45 | */
46 | AsyncLoadImage get(String path){
47 | new AsyncLoadImage(path, this)
48 | }
49 |
50 | /**
51 | * 立即获取一张图片
52 | */
53 | Image sync(String path){
54 | return new Image(getTexture(path))
55 | }
56 |
57 | /**
58 | * 立即获取一张drawable
59 | */
60 | SpriteDrawable getDrawable(String path) {
61 | new SpriteDrawable(new Sprite(getTexture(path)))
62 | }
63 |
64 | SpriteDrawable defaultDrawable(Color color = Color.WHITE){
65 | defaultSpriteDrawable.tint(color)
66 | }
67 |
68 | /**
69 | * 更新资源管理器
70 | */
71 | boolean act(){
72 | return assetManager.update()
73 | }
74 |
75 | /**
76 | * 立即获取一张纹理
77 | */
78 | Texture getTexture(String path){
79 | assetManager.load(path, Texture.class)
80 | while(!assetManager.update()){}
81 | return assetManager.get(path, Texture.class)
82 | }
83 |
84 | /**
85 | * 预加载纹理
86 | */
87 | void preInit(String[] paths){
88 | for(String path : paths)
89 | assetManager.load(path, Texture.class)
90 | }
91 |
92 | /**卸载全部纹理*/
93 | void dispose(){
94 | assetManager.dispose()
95 | text.dispose()
96 | defaultSpriteDrawable.sprite.texture.dispose()
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/manager/TextManager.groovy:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.manager
2 |
3 | import com.badlogic.gdx.Gdx
4 | import com.badlogic.gdx.graphics.g2d.BitmapFont
5 | import com.badlogic.gdx.graphics.g2d.GlyphLayout
6 | import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator
7 | import com.badlogic.gdx.utils.Pools
8 | import groovy.transform.CompileStatic
9 | import team.rpsg.html.manager.widget.Label
10 | import team.rpsg.lazyFont.LazyBitmapFont
11 |
12 | @CompileStatic
13 | class TextManager {
14 | private Map map = new HashMap<>()
15 |
16 | FreeTypeFontGenerator NORMAL_GENERATOR = new FreeTypeFontGenerator(Gdx.files.internal("font/xyj.ttf"))
17 | FreeTypeFontGenerator ENGLISH_GENERATOR = new FreeTypeFontGenerator(Gdx.files.internal("font/Coold.ttf"))
18 |
19 |
20 | LazyBitmapFont get(int fontSize, FreeTypeFontGenerator gen) {
21 | LazyBitmapFont font = null
22 |
23 | for(Param k : map.keySet())
24 | if(k.size == fontSize && k.gen == gen)
25 | font = map.get(k)
26 |
27 | if (font == null) {
28 | font = gen == null ? new LazyBitmapFont(NORMAL_GENERATOR, fontSize) : new LazyBitmapFont(gen, fontSize)
29 | map.put(new Param(fontSize, gen), font)
30 | }
31 |
32 | return font
33 | }
34 |
35 | LazyBitmapFont get(int fontSize) {
36 | return get(fontSize, null)
37 | }
38 |
39 | Label getLabel(int fontSize) {
40 | return getLabel("", fontSize)
41 | }
42 |
43 | Label getLabel(Object text, int fontSize) {
44 | return getLabel(text, fontSize, null)
45 | }
46 |
47 | Label getLabel(Object text, int fontSize, FreeTypeFontGenerator gen) {
48 | com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle ls = new com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle()
49 | ls.font = gen == null ? get(fontSize) : get(fontSize, gen)
50 |
51 | return new Label(text.toString(), ls)
52 | }
53 |
54 |
55 | private static class Param{
56 | int size
57 | FreeTypeFontGenerator gen
58 |
59 | Param(int size, FreeTypeFontGenerator gen) {
60 | this.size = size
61 | this.gen = gen
62 | }
63 |
64 | }
65 |
66 | int getTextWidth(String str, int size) {
67 | return getTextWidth(get(size),str)
68 |
69 | }
70 |
71 | static int getTextWidth(BitmapFont font, String str){
72 | GlyphLayout layout = Pools.obtain(GlyphLayout.class)
73 | layout.setText(font, str)
74 | return (int) layout.width
75 | }
76 |
77 | void dispose(){
78 | NORMAL_GENERATOR.dispose()
79 | ENGLISH_GENERATOR.dispose()
80 |
81 | map.each {k, v -> v.dispose()}
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/manager/widget/AsyncLoadImage.java:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.manager.widget;
2 |
3 | import com.badlogic.gdx.assets.loaders.TextureLoader.TextureParameter;
4 | import com.badlogic.gdx.graphics.Texture;
5 | import com.badlogic.gdx.graphics.Texture.TextureFilter;
6 | import com.badlogic.gdx.graphics.g2d.Batch;
7 | import com.badlogic.gdx.graphics.g2d.TextureRegion;
8 | import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
9 | import team.rpsg.gdxQuery.CustomRunnable;
10 | import team.rpsg.html.manager.ResourceManager;
11 |
12 | /**
13 | * GDX-RPG 异步加载纹理类
14 | * 启用另一个OpenGL线程来达到异步加载纹理的效果,在加载的过程中,当前的图片是无法读取宽/高度的。
15 | */
16 | public class AsyncLoadImage extends Image {
17 |
18 | private boolean loaded = false, loading = false;
19 | private int originAlignment;
20 | private String texturePath;
21 |
22 | private CustomRunnable _onLoaded = null;
23 |
24 | private ResourceManager resourceManager = null;
25 |
26 |
27 | private AsyncLoadImage() {}
28 |
29 | public AsyncLoadImage(String texturePath, ResourceManager resourceManager) {
30 | this.texturePath = texturePath;
31 | this.resourceManager = resourceManager;
32 |
33 | loadTexture();
34 | }
35 |
36 | public AsyncLoadImage(String texturePath, CustomRunnable onLoaded, ResourceManager resourceManager) {
37 | this(texturePath, resourceManager);
38 | this._onLoaded = onLoaded;
39 |
40 | loadTexture();
41 | }
42 |
43 | /**
44 | * 初始化
45 | */
46 | private void init(){
47 | /**如果开启纹理过滤,则设置*/
48 | ((TextureRegionDrawable)getDrawable()).getRegion().getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
49 |
50 | /**重置自身大小*/
51 | if (getWidth() == 0)
52 | setWidth(getDrawable().getMinWidth());
53 | if (getHeight() == 0)
54 | setHeight(getDrawable().getMinHeight());
55 | if (originAlignment != -1)
56 | setOrigin(originAlignment);
57 |
58 | }
59 |
60 | /**当该纹理被调用绘制时,才开始懒加载纹理,否则歇着*/
61 | public void draw(Batch batch, float parentAlpha) {
62 | super.draw(batch, parentAlpha);
63 | }
64 |
65 |
66 | TextureParameter parameter = new TextureParameter();
67 | {
68 | /**当纹理加载完成后的回调*/
69 | parameter.loadedCallback = (assetManager, fileName, type) -> {
70 | loaded = true;
71 |
72 | setDrawable(new TextureRegionDrawable(new TextureRegion(resourceManager.assetManager.get(texturePath, Texture.class))));
73 | init();
74 |
75 | if(_onLoaded != null)
76 | _onLoaded.run(AsyncLoadImage.this);
77 | };
78 | }
79 |
80 | /**加载纹理**/
81 | public void loadTexture(){
82 | if(texturePath == null || loading)
83 | return;
84 |
85 | loading = true;
86 |
87 | resourceManager.assetManager.load(texturePath, Texture.class, parameter);
88 | }
89 |
90 | /**是否加载完成*/
91 | public boolean loaded(){
92 | return loaded;
93 | }
94 |
95 | public void setOrigin(int alignment) {
96 | super.setOrigin(originAlignment = alignment);
97 | }
98 |
99 | /**
100 | * 异步加载一个drawable
101 | */
102 | public void setDrawableAsync(String drawablePath) {
103 | new AsyncLoadImage(drawablePath, that -> {
104 | setDrawable(that.getDrawable());
105 | init();
106 | }, resourceManager).loadTexture();
107 | }
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/manager/widget/AutoSizeContainer.java:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.manager.widget;
2 |
3 | import com.badlogic.gdx.scenes.scene2d.ui.Container;
4 | import team.rpsg.html.dom.Dom;
5 |
6 | public class AutoSizeContainer extends Container {
7 |
8 | public AutoSizeContainer(Dom dom){
9 | super(dom);
10 | }
11 |
12 | public float getWidth() {
13 | return getPrefWidth();
14 | }
15 |
16 | public float getHeight() {
17 | return getPrefHeight();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/manager/widget/Image.java:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.manager.widget;
2 |
3 | import com.badlogic.gdx.graphics.Texture;
4 | import com.badlogic.gdx.graphics.Texture.TextureFilter;
5 | import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
6 | import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
7 | import team.rpsg.gdxQuery.TypedGdxQuery;
8 |
9 | /**
10 | * GDX-RPG 图片类
11 | */
12 | public class Image extends com.badlogic.gdx.scenes.scene2d.ui.Image{
13 | public TypedGdxQuery query(){
14 | return new TypedGdxQuery<>(this);
15 | }
16 |
17 | public Image (Texture texture){
18 | super(texture);
19 | texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
20 | }
21 |
22 | public void setDrawable(Drawable drawable) {
23 | super.setDrawable(drawable);
24 |
25 | if(drawable == null)
26 | return;
27 |
28 | if(drawable instanceof TextureRegionDrawable)
29 | ((TextureRegionDrawable)drawable).getRegion().getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
30 | }
31 |
32 | public Image (Drawable drawable){
33 | super(drawable);
34 | }
35 |
36 | public Image(){
37 | super((Drawable)null);
38 | }
39 |
40 | /**使用整数坐标,以免导致纹理失真*/
41 | public void setWidth(float width) {
42 | super.setWidth((int)width);
43 | }
44 |
45 | public void setHeight(float height) {
46 | super.setHeight((int)height);
47 | }
48 |
49 | public void setX(float x) {
50 | super.setX((int)x);
51 | }
52 |
53 | public void setY(float y) {
54 | super.setY((int)y);
55 | }
56 |
57 | public void setPosition(float x, float y) {
58 | super.setPosition((int)x, (int)y);
59 | }
60 |
61 | public boolean isTransparent() {
62 | return getColor().a <= 0;
63 | }
64 |
65 | public Texture getTexture() {
66 | if(getDrawable() instanceof TextureRegionDrawable)
67 | return ((TextureRegionDrawable) getDrawable()).getRegion().getTexture();
68 |
69 | return null;
70 | }
71 |
72 | public Image disableTouch(){
73 | setTouchable(null);
74 | return this;
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/util/AlignParser.groovy:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.util
2 |
3 | import com.badlogic.gdx.utils.Align
4 | import groovy.transform.CompileStatic
5 |
6 | @CompileStatic
7 | class AlignParser {
8 | static int textAlign(Object align, int defaultAlign = Align.left){
9 | if(!align || align.toString().length() == 0)
10 | return defaultAlign
11 |
12 | def s = align.toString()
13 |
14 | switch (s.toLowerCase()){
15 | case "left": return Align.left
16 | case "right": return Align.right
17 | case "center": return Align.center
18 | }
19 |
20 | return defaultAlign
21 | }
22 |
23 | static int verticalAlign(Object align, int defaultAlign = Align.bottom){
24 |
25 | if(!align || align.toString().length() == 0)
26 | return defaultAlign
27 |
28 | def s = align.toString()
29 |
30 | switch (s.toLowerCase()){
31 | case "top": return Align.top
32 | case "middle": return Align.center
33 | case "center": return Align.center
34 | case "bottom": return Align.bottom
35 | }
36 |
37 | return defaultAlign
38 | }
39 |
40 | static int join(int lr, int tb){
41 | if(lr == Align.center && tb == Align.center)
42 | return Align.center
43 | if(lr == Align.center)
44 | return tb
45 | if(tb == Align.center)
46 | return lr
47 |
48 | return tb | lr
49 | }
50 |
51 | static String toString(Integer align){
52 | switch (align){
53 | case Align.center : return "center"
54 | case Align.left : return "left"
55 | case Align.right : return "right"
56 | case Align.bottom : return "bottom"
57 | case Align.bottomLeft : return "bottomLeft"
58 | case Align.bottomRight : return "bottomRight"
59 | case Align.top : return "top"
60 | case Align.topLeft : return "topLeft"
61 | case Align.topRight : return "topRight"
62 | default: return "UNKNOWN(" + align + ")"
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/util/BoxParser.groovy:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.util
2 |
3 |
4 | import com.badlogic.gdx.scenes.scene2d.ui.Value
5 | import com.badlogic.gdx.utils.Align
6 | import com.steadystate.css.dom.CSSValueImpl
7 | import groovy.transform.CompileStatic
8 | import team.rpsg.html.dom.Dom
9 |
10 | @CompileStatic
11 | class BoxParser {
12 | static void parsePadding(Dom dom){
13 | float padLeft = 0, padRight = 0, padTop = 0, padBottom = 0
14 |
15 | def padAll = dom.style("padding", null, {r -> r}, false) as CSSValueImpl
16 |
17 | if(padAll) {
18 |
19 | List values = []
20 |
21 | if (padAll.value instanceof List){
22 | values = padAll.value as List
23 | }else{
24 | values << padAll
25 | }
26 |
27 | if(values.size() == 1) {
28 | def v = SizeParser.parse(values[0])
29 | if(v)
30 | padLeft = padRight = padTop = padBottom = v.get(dom)
31 | }else if(values.size() == 2){
32 | def v0 = SizeParser.parse(values[0]), v1 = SizeParser.parse(values[1])
33 | if(v0)
34 | padTop = padBottom = v0.get(dom)
35 | if(v1)
36 | padLeft = padRight = v1.get(dom)
37 | }
38 | }
39 |
40 | padLeft = parseValueInt(dom, "padding-left", padLeft)
41 | padRight = parseValueInt(dom, "padding-right", padRight)
42 | padTop = parseValueInt(dom, "padding-top", padTop)
43 | padBottom = parseValueInt(dom, "padding-bottom", padBottom)
44 |
45 | dom.pad(padTop, padLeft, padBottom, padRight)
46 |
47 | }
48 |
49 | /**
50 | * @return parsed
51 | */
52 | public static final int NEEDS_SET_ALIGN = 1
53 |
54 | static int parseMargin(Dom dom){
55 | if(!dom.parentContainer)
56 | return -1
57 |
58 | float mLeft = 0, mRight = 0, mTop = 0, mBottom = 0
59 | boolean mLeftAuto = false, mRightAuto = false
60 |
61 | def mAll = dom.style("margin", null, {r -> r}, false) as CSSValueImpl
62 |
63 | if(mAll) {
64 |
65 | List values = []
66 |
67 | if (mAll.value instanceof List){
68 | values = mAll.value as List
69 | }else{
70 | values << mAll
71 | }
72 |
73 | if(values.size() == 1) {
74 | def v = SizeParser.parse(values[0], true)
75 | if(v instanceof SizeParser.AutoValue){
76 | mLeftAuto = mRightAuto = true
77 | mLeft = mRight = 0
78 | } else if(v){
79 | mLeft = mRight = mTop = mBottom = v.get(dom)
80 | }
81 | }else if(values.size() == 2){
82 | def v0 = SizeParser.parse(values[0]), v1 = SizeParser.parse(values[1], true)
83 | if(v0)
84 | mTop = mBottom = v0.get(dom)
85 |
86 | if(v1 instanceof SizeParser.AutoValue){
87 | mLeftAuto = mRightAuto = true
88 | mLeft = mRight = 0
89 | }else if(v1){
90 | mLeft = mRight = v1.get(dom)
91 | }
92 | }
93 | }
94 |
95 | Object _l = parseValue(dom, "margin-left", mLeft)
96 | if(_l instanceof SizeParser.AutoValue){
97 | mLeftAuto = true
98 | mLeft = 0
99 | } else {
100 | mLeft = _l as int
101 | }
102 |
103 | Object _r = parseValue(dom, "margin-right", mRight)
104 | if(_r instanceof SizeParser.AutoValue){
105 | mRightAuto = true
106 | mRight = 0
107 | } else {
108 | mRight = _r as int
109 | }
110 |
111 | mTop = parseValueInt(dom, "margin-top", mTop)
112 | mBottom = parseValueInt(dom, "margin-bottom", mBottom)
113 |
114 | dom.parentContainer.pad(mTop, mLeft, mBottom, mRight)
115 |
116 | int align = Align.left
117 | if(mLeftAuto)
118 | align = Align.right
119 | if(mLeftAuto && mRightAuto)
120 | align = Align.center
121 |
122 | def result = 0
123 | if(align != Align.left)
124 | result = NEEDS_SET_ALIGN
125 |
126 | dom.boxAlign = align
127 |
128 | return result
129 |
130 | }
131 |
132 | private static int parseValueInt(Dom dom, String name, float orDefault = 0){
133 | def result = dom.style(name, orDefault, SizeParser.&parse, false) as Value
134 |
135 | if(!result)
136 | return orDefault
137 |
138 | return result.get(dom)
139 | }
140 |
141 | private static Object parseValue(Dom dom, String name, float orDefault = 0){
142 | def result = dom.style(name, orDefault, {p -> SizeParser.parse(p, true)}, false) as Value
143 |
144 | if(!result)
145 | return orDefault
146 |
147 | if(result instanceof SizeParser.AutoValue)
148 | return result
149 |
150 | return result.get(dom)
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/util/DomParser.groovy:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.util
2 |
3 | import groovy.transform.CompileStatic
4 | import org.jsoup.nodes.Element
5 | import org.jsoup.nodes.Node
6 | import org.jsoup.nodes.TextNode
7 | import team.rpsg.html.dom.Dom
8 | import team.rpsg.html.dom.HTMLDomPreset
9 | import team.rpsg.html.dom.UnknownDom
10 | import team.rpsg.html.dom.node.Image
11 | import team.rpsg.html.dom.node.Text
12 |
13 |
14 | @CompileStatic
15 | class DomParser {
16 | /**
17 | * parse jsoup node to renderable GDX-HTML dom.
18 | */
19 | static Dom parse(Node node){
20 | switch (node.class){
21 |
22 | case TextNode.class:
23 | if((node as TextNode).text().trim().length() == 0)
24 | return null
25 | return new Text(node)
26 |
27 | case Element.class:
28 | def ele = node as Element
29 |
30 | def tagName = ele.tagName().toLowerCase()
31 | switch (tagName){
32 | case "img": return new Image(ele)
33 | }
34 |
35 |
36 | try{
37 | return HTMLDomPreset.valueOf(ele.tagName().toUpperCase()).dom(node)
38 | }catch (ignored){
39 | return new UnknownDom(node)
40 | }
41 |
42 |
43 | default:
44 | return new UnknownDom(node)
45 |
46 | }
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/util/PathParser.groovy:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.util
2 |
3 | import com.badlogic.gdx.Gdx
4 | import com.badlogic.gdx.Net
5 | import groovy.transform.CompileStatic
6 | import org.jsoup.nodes.Document
7 |
8 |
9 | /**
10 | * parsePadding path
11 | */
12 | @CompileStatic
13 | class PathParser {
14 | /**
15 | * parsePadding path by document baseURI, return absolute(assets root) path
16 | *
17 | * assets
18 | * ├── img
19 | * │ └── 1.jpg
20 | * ├── html
21 | * │ ├── 1.html
22 | * │ └── 2.html
23 | *
24 | * assert PathParser.parsePadding(doc, "../img/1.jpg") == "/img/1.jpg";
25 | * assert PathParser.parsePadding(doc, "2.html") == "/html/2.html";
26 | */
27 | static String parse(Document document, String path){
28 | return new URI(document.baseUri() ?: "/").resolve(new URI(path)).toString()
29 | }
30 |
31 |
32 | static void get(Document document, String path, String charset, Closure callback){
33 | if(!charset || charset.length() == 0)
34 | charset = "utf-8"
35 |
36 | def uri = new URI(document.baseUri() ?: "/").resolve(new URI(path))
37 |
38 | try{
39 | def request = new Net.HttpRequest(url: uri.toURL().toString(), method: "GET")
40 |
41 | Gdx.net.sendHttpRequest(request, new Net.HttpResponseListener(){
42 | void handleHttpResponse(Net.HttpResponse httpResponse) {
43 | callback(httpResponse.resultAsString, true)
44 | }
45 | void failed(Throwable t) {
46 | t.printStackTrace()
47 | }
48 | void cancelled() {}
49 | })
50 |
51 |
52 | }catch(ignored){
53 | callback(Gdx.files.internal(uri.toString()).readString(charset), false)
54 | }
55 |
56 |
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/util/SizeParser.groovy:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.util
2 |
3 | import com.badlogic.gdx.scenes.scene2d.Actor
4 | import com.badlogic.gdx.scenes.scene2d.ui.Table
5 | import com.badlogic.gdx.scenes.scene2d.ui.Value
6 | import com.badlogic.gdx.scenes.scene2d.utils.Layout
7 | import com.steadystate.css.dom.CSSValueImpl
8 | import com.steadystate.css.parser.LexicalUnitImpl
9 | import groovy.transform.CompileStatic
10 | import team.rpsg.html.dom.Dom
11 |
12 | /**
13 | * unit(px or %) parser
14 | */
15 | @CompileStatic
16 | class SizeParser {
17 | /**
18 | * font-size: 22px;
19 | * font-size: auto;
20 | */
21 | static int parseFontPX(Object property){
22 |
23 | if(property.toString().indexOf('%') > 0)
24 | return 16
25 | if(property.toString().equals("0"))
26 | return 0
27 | if(property.toString().indexOf('px') > 0)
28 | return property.toString().replace("px", "").toInteger()?: 16
29 | if(property.toString().indexOf('rem') > 0)
30 | return ((property.toString().replace("rem", "").toFloat() * 16f)?: 16) as int
31 | if(property.toString().indexOf('em') > 0)
32 | return ((property.toString().replace("em", "").toFloat() * 16f)?: 16) as int
33 |
34 | return 16
35 |
36 | }
37 |
38 | /**
39 | * width: 80%;
40 | * width: 65px;
41 | * width: auto;
42 | */
43 | static Value parse(Object property, parseAutoValue = false, Closure valueParser = this.&percentInnerWidth){
44 |
45 | if(property && property.toString().equalsIgnoreCase("auto")){
46 | if(parseAutoValue)
47 | return new AutoValue()
48 | return null
49 | }
50 |
51 | if(property instanceof Float)
52 | return new Value.Fixed(property.toFloat())
53 |
54 | if(property instanceof CSSValueImpl && property.value instanceof LexicalUnitImpl){
55 | def unit = property.value as LexicalUnitImpl
56 |
57 | if(unit.lexicalUnitType == 17)
58 | return new Value.Fixed(unit.floatValue)
59 |
60 | if(unit.lexicalUnitType == 13 || property.toString().equals("0"))
61 | return new Value.Fixed(0)
62 |
63 | if(unit.lexicalUnitType == 23)
64 | return valueParser((unit.floatValue / 100f) as float) as Value
65 | }
66 |
67 | if(property instanceof String){
68 | if(property.indexOf('%') > 0)
69 | return valueParser((property.toString().replace("%", "").toFloat() / 100f) as float) as Value
70 | if(property.indexOf("px") > 0)
71 | return new Value.Fixed(property.toString().replace("px", "").toInteger())
72 | }
73 |
74 |
75 | return null
76 | }
77 |
78 | static Value percentInnerWidth(float p){
79 | return new percentInnerValue() {
80 | float getPercent() {
81 | p
82 | }
83 |
84 | float get (Actor actor) {
85 | if(actor instanceof Dom)
86 | return (actor as Dom).innerWidth * p
87 |
88 | return (actor as Table).width * p
89 | }
90 | }
91 | }
92 |
93 | static Value percentInnerHeight(float p){
94 | return new percentInnerValue() {
95 | float getPercent() {
96 | p
97 | }
98 |
99 | float get (Actor actor) {
100 | if(actor instanceof Dom)
101 | return (actor as Dom).innerHeight * p
102 |
103 | return (actor as Table).height * p
104 | }
105 | }
106 | }
107 |
108 | static final Value innerWidth = percentInnerWidth(1)
109 | static final Value innerHeight = percentInnerHeight(1)
110 |
111 | static class AutoValue extends Value{
112 | float get(Actor context) {
113 | return 0
114 | }
115 | }
116 |
117 | abstract static class percentInnerValue extends Value{
118 | abstract float getPercent()
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/util/StyleParser.groovy:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.util
2 |
3 | import com.steadystate.css.dom.CSSStyleDeclarationImpl
4 | import com.steadystate.css.parser.CSSOMParser
5 | import com.steadystate.css.parser.SACParserCSS3
6 | import groovy.transform.CompileStatic
7 | import org.w3c.css.sac.InputSource
8 |
9 | @CompileStatic
10 | class StyleParser {
11 | /**
12 | * @param styles string, like "font-size: 20px; css-prop: value; ..."
13 | * @return
14 | */
15 | static CSSStyleDeclarationImpl parse(String stylesString){
16 | (CSSStyleDeclarationImpl)new CSSOMParser(new SACParserCSS3()).parseStyleDeclaration(new InputSource(new StringReader(stylesString)))
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/html/src/team/rpsg/html/util/Timer.java:
--------------------------------------------------------------------------------
1 | package team.rpsg.html.util;
2 |
3 | import com.badlogic.gdx.Gdx;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | /**
9 | * 延时运行工具
10 | * LibGDX本身也有个{@link Timer}工具,但是他的实现是靠多线程的,为了避免吃到意外的屎,只好造一个轮子了2333,该轮子不依靠线程,而是在主线程的主循环里工作。
11 | */
12 | public class Timer {
13 | List list = new ArrayList<>();
14 | List addList = new ArrayList<>();
15 |
16 | public static final int FOREVER = -1;
17 | boolean pause = false;
18 |
19 | public Task add(TimeType type, int delay, int repeat, Runnable run) {
20 | Task task = new Task();
21 | task.run = run;
22 | task.time = task.originTime = delay;
23 | task.repeat = repeat;
24 | task.type = type;
25 | addList.add(task);
26 |
27 | return task;
28 | }
29 |
30 | public Task add(TimeType type, int delay, Runnable run) {
31 | return add(type, delay, 1, run);
32 | }
33 |
34 | public void act() {
35 | float delta = Gdx.graphics.getRawDeltaTime();
36 |
37 | if(!addList.isEmpty()){
38 | list.addAll(addList);
39 | addList.clear();
40 | }
41 |
42 | if(!list.isEmpty()){
43 | List removeList = new ArrayList<>();
44 | for(Task timer : list){
45 | if((timer.type == TimeType.frame && timer.time -- < 0) || (timer.type == TimeType.millisecond && (timer.time -= delta * 1000) < 0)){
46 | timer.run.run();
47 | if(timer.repeat != FOREVER && -- timer.repeat == 0)
48 | removeList.add(timer.done());
49 | else
50 | timer.time = timer.originTime;
51 | }
52 | }
53 |
54 | list.removeAll(removeList);
55 | }
56 | }
57 |
58 | public void pause() {
59 | pause = true;
60 | }
61 |
62 | public void resume() {
63 | pause = false;
64 | }
65 |
66 | public void remove(Task timer) {
67 | list.remove(timer);
68 | }
69 |
70 | public void then(Runnable o) {
71 | add(TimeType.frame, 0, o);
72 | }
73 |
74 | public static class Task{
75 | public int time, originTime;
76 | public int repeat = 1;
77 | public Runnable run;
78 | public TimeType type;
79 |
80 | public boolean done = false;
81 |
82 | public Task done(){
83 | done = true;
84 | return this;
85 | }
86 | }
87 |
88 | public enum TimeType{
89 | frame, millisecond
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/readme/show1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dingjibang/GDX-HTML/a30720ed6ba3eded776a54e9095e6fee17416ae7/readme/show1.png
--------------------------------------------------------------------------------
/readme/show2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dingjibang/GDX-HTML/a30720ed6ba3eded776a54e9095e6fee17416ae7/readme/show2.png
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include 'desktop', 'android', 'core'
2 | include 'html'
3 |
4 |
--------------------------------------------------------------------------------