Skip to content

Commit c4c95bf

Browse files
committed
feat: support text selection
1 parent eca70c3 commit c4c95bf

6 files changed

Lines changed: 1311 additions & 69 deletions

File tree

litehtml-sys/csrc/litehtml_c.cpp

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include "litehtml_c.h"
99
#include <litehtml.h>
10+
#include <litehtml/render_item.h>
1011
#include <cstring>
1112
#include <string>
1213

@@ -1123,4 +1124,154 @@ int lh_document_media_changed(lh_document_t* doc)
11231124
return internal->doc->media_changed() ? 1 : 0;
11241125
}
11251126

1127+
/* --------------------------------------------------------------------------
1128+
* Element introspection
1129+
* -------------------------------------------------------------------------- */
1130+
1131+
lh_element_t* lh_element_parent(lh_element_t* el)
1132+
{
1133+
if (!el) return nullptr;
1134+
auto* elem = reinterpret_cast<litehtml::element*>(el);
1135+
auto parent = elem->parent();
1136+
if (!parent) return nullptr;
1137+
return reinterpret_cast<lh_element_t*>(parent.get());
1138+
}
1139+
1140+
int lh_element_children_count(lh_element_t* el)
1141+
{
1142+
if (!el) return 0;
1143+
auto* elem = reinterpret_cast<litehtml::element*>(el);
1144+
return static_cast<int>(elem->children().size());
1145+
}
1146+
1147+
lh_element_t* lh_element_child_at(lh_element_t* el, int index)
1148+
{
1149+
if (!el) return nullptr;
1150+
auto* elem = reinterpret_cast<litehtml::element*>(el);
1151+
const auto& kids = elem->children();
1152+
if (index < 0 || index >= static_cast<int>(kids.size()))
1153+
return nullptr;
1154+
auto it = kids.begin();
1155+
std::advance(it, index);
1156+
return reinterpret_cast<lh_element_t*>(it->get());
1157+
}
1158+
1159+
int lh_element_is_text(lh_element_t* el)
1160+
{
1161+
if (!el) return 0;
1162+
auto* elem = reinterpret_cast<litehtml::element*>(el);
1163+
return elem->is_text() ? 1 : 0;
1164+
}
1165+
1166+
uintptr_t lh_element_get_font(lh_element_t* el)
1167+
{
1168+
if (!el) return 0;
1169+
auto* elem = reinterpret_cast<litehtml::element*>(el);
1170+
return elem->css().get_font();
1171+
}
1172+
1173+
float lh_element_get_font_size(lh_element_t* el)
1174+
{
1175+
if (!el) return 0.0f;
1176+
auto* elem = reinterpret_cast<litehtml::element*>(el);
1177+
return elem->css().get_font_size();
1178+
}
1179+
1180+
void lh_element_get_placement(lh_element_t* el, lh_position_t* pos)
1181+
{
1182+
if (!el || !pos) return;
1183+
auto* elem = reinterpret_cast<litehtml::element*>(el);
1184+
litehtml::position p = elem->get_placement();
1185+
*pos = to_c(p);
1186+
}
1187+
1188+
void lh_element_get_text(lh_element_t* el,
1189+
void (*cb)(void* ctx, const char* text),
1190+
void* ctx)
1191+
{
1192+
if (!el || !cb) return;
1193+
auto* elem = reinterpret_cast<litehtml::element*>(el);
1194+
litehtml::string text;
1195+
elem->get_text(text);
1196+
cb(ctx, text.c_str());
1197+
}
1198+
1199+
lh_element_t* lh_document_get_element_by_point(lh_document_t* doc,
1200+
float x, float y,
1201+
float client_x, float client_y)
1202+
{
1203+
if (!doc) return nullptr;
1204+
auto* internal = reinterpret_cast<lh_document_internal*>(doc);
1205+
auto root_render = internal->doc->root_render();
1206+
if (!root_render) return nullptr;
1207+
auto el = root_render->get_element_by_point(x, y, client_x, client_y,
1208+
[](const std::shared_ptr<litehtml::render_item>&) { return true; });
1209+
if (!el) return nullptr;
1210+
return reinterpret_cast<lh_element_t*>(el.get());
1211+
}
1212+
1213+
/* --------------------------------------------------------------------------
1214+
* Inline box helpers
1215+
*
1216+
* get_inline_boxes() returns local-coordinate boxes from the render item.
1217+
* We compute the same parent-chain offset that get_placement() uses, then
1218+
* apply it to each box so callers get absolute document coordinates.
1219+
* -------------------------------------------------------------------------- */
1220+
1221+
/* Compute parent-chain offset: placement.{x,y} - m_pos.{x,y} */
1222+
static void compute_ri_offset(const std::shared_ptr<litehtml::render_item>& ri,
1223+
float& ox, float& oy)
1224+
{
1225+
litehtml::position placement = ri->get_placement();
1226+
litehtml::position pos = ri->pos();
1227+
ox = placement.x - pos.x;
1228+
oy = placement.y - pos.y;
1229+
}
1230+
1231+
int lh_element_get_inline_boxes_count(lh_element_t* el)
1232+
{
1233+
if (!el) return 0;
1234+
auto* elem = reinterpret_cast<litehtml::element*>(el);
1235+
auto ri = elem->get_render_item();
1236+
if (!ri) return 0;
1237+
litehtml::position::vector boxes;
1238+
ri->get_inline_boxes(boxes);
1239+
return static_cast<int>(boxes.size());
1240+
}
1241+
1242+
void lh_element_get_inline_box_at(lh_element_t* el, int index, lh_position_t* pos)
1243+
{
1244+
if (!el || !pos) return;
1245+
auto* elem = reinterpret_cast<litehtml::element*>(el);
1246+
auto ri = elem->get_render_item();
1247+
if (!ri) return;
1248+
1249+
litehtml::position::vector boxes;
1250+
ri->get_inline_boxes(boxes);
1251+
if (index < 0 || index >= static_cast<int>(boxes.size()))
1252+
return;
1253+
1254+
float ox, oy;
1255+
compute_ri_offset(ri, ox, oy);
1256+
1257+
litehtml::position box = boxes[index];
1258+
box.x += ox;
1259+
box.y += oy;
1260+
*pos = to_c(box);
1261+
}
1262+
1263+
int lh_element_get_text_align(lh_element_t* el)
1264+
{
1265+
if (!el) return 0;
1266+
auto* elem = reinterpret_cast<litehtml::element*>(el);
1267+
return static_cast<int>(elem->css().get_text_align());
1268+
}
1269+
1270+
float lh_element_get_line_height(lh_element_t* el)
1271+
{
1272+
if (!el) return 0.0f;
1273+
auto* elem = reinterpret_cast<litehtml::element*>(el);
1274+
return elem->css().line_height().computed_value;
1275+
}
1276+
11261277
} /* extern "C" */

litehtml-sys/csrc/litehtml_c.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,53 @@ void lh_document_add_stylesheet(lh_document_t* doc,
335335
/* Get the root element of the document. Returns NULL if doc is NULL. */
336336
lh_element_t* lh_document_root(lh_document_t* doc);
337337

338+
/* --------------------------------------------------------------------------
339+
* Element introspection
340+
* -------------------------------------------------------------------------- */
341+
342+
/* Get the parent element. Returns NULL for root or if el is NULL. */
343+
lh_element_t* lh_element_parent(lh_element_t* el);
344+
345+
/* Number of child elements. Returns 0 if el is NULL. */
346+
int lh_element_children_count(lh_element_t* el);
347+
348+
/* Get the child at the given index. Returns NULL if out of bounds. */
349+
lh_element_t* lh_element_child_at(lh_element_t* el, int index);
350+
351+
/* Returns non-zero if the element is a text node. */
352+
int lh_element_is_text(lh_element_t* el);
353+
354+
/* Get the font handle from the element's computed CSS. Returns 0 on error. */
355+
uintptr_t lh_element_get_font(lh_element_t* el);
356+
357+
/* Get the font size from the element's computed CSS. Returns 0.0 on error. */
358+
float lh_element_get_font_size(lh_element_t* el);
359+
360+
/* Get the element's absolute pixel bounding box after layout. */
361+
void lh_element_get_placement(lh_element_t* el, lh_position_t* pos);
362+
363+
/* Get the element's recursive text content via callback. */
364+
void lh_element_get_text(lh_element_t* el,
365+
void (*cb)(void* ctx, const char* text),
366+
void* ctx);
367+
368+
/* Hit testing: find the deepest element at document coordinates (x, y). */
369+
lh_element_t* lh_document_get_element_by_point(lh_document_t* doc,
370+
float x, float y,
371+
float client_x, float client_y);
372+
373+
/* Number of per-line inline boxes for a rendered element (0 if not inline). */
374+
int lh_element_get_inline_boxes_count(lh_element_t* el);
375+
376+
/* Get the i-th inline box in absolute document coordinates. */
377+
void lh_element_get_inline_box_at(lh_element_t* el, int index, lh_position_t* pos);
378+
379+
/* Get the computed text-align value (0=left, 1=right, 2=center, 3=justify). */
380+
int lh_element_get_text_align(lh_element_t* el);
381+
382+
/* Get the computed line-height in pixels. */
383+
float lh_element_get_line_height(lh_element_t* el);
384+
338385
/* Parse an HTML fragment and append the resulting elements as children of parent.
339386
If replace_existing is non-zero, existing children are removed first.
340387
Requires a subsequent render() to update layout. */

0 commit comments

Comments
 (0)