mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-07-10 17:53:23 +00:00
Merge branch 'copilot/update-draw-single-line-text-function' of https://github.com/kovidgoyal/kitty
This commit is contained in:
commit
de3eda67d3
10 changed files with 117 additions and 14 deletions
|
|
@ -64,6 +64,7 @@ void cocoa_update_menu_bar_title(PyObject*);
|
|||
size_t cocoa_get_workspace_ids(void *w, size_t *workspace_ids, size_t array_sz);
|
||||
monotonic_t cocoa_cursor_blink_interval(void);
|
||||
bool cocoa_render_line_of_text(const char *text, const color_type fg, const color_type bg, uint8_t *rgba_output, const size_t width, const size_t height);
|
||||
size_t cocoa_text_width_for_single_line(const char *text, const size_t height);
|
||||
extern uint8_t* render_single_ascii_char_as_mask(const char ch, size_t *result_width, size_t *result_height);
|
||||
void get_cocoa_key_equivalent(uint32_t, int, char *key, size_t key_sz, int*);
|
||||
void set_cocoa_pending_action(CocoaPendingAction action, const char*);
|
||||
|
|
|
|||
|
|
@ -974,6 +974,21 @@ cocoa_render_line_of_text(const char *text, const color_type fg, const color_typ
|
|||
return true;
|
||||
}
|
||||
|
||||
size_t
|
||||
cocoa_text_width_for_single_line(const char *text, const size_t height) {
|
||||
if (!text || !text[0]) return 0;
|
||||
if (!ensure_ui_font(height)) return 0;
|
||||
NSAttributedString *str = [[NSAttributedString alloc] initWithString:@(text) attributes:@{(NSString *)kCTFontAttributeName: (__bridge id)system_ui_font}];
|
||||
if (!str) return 0;
|
||||
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)str);
|
||||
[str release];
|
||||
if (!line) return 0;
|
||||
CGFloat ascent, descent, leading;
|
||||
double width = CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
|
||||
CFRelease(line);
|
||||
return (size_t)ceil(width);
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
render_single_ascii_char_as_mask(const char ch, size_t *result_width, size_t *result_height) {
|
||||
if (!ensure_ui_font(*result_height)) { PyErr_SetString(PyExc_RuntimeError, "failed to create UI font"); return NULL; }
|
||||
|
|
|
|||
|
|
@ -1846,7 +1846,7 @@ def start_drag_with_data(
|
|||
operations: int = GLFW_DRAG_OPERATION_MOVE
|
||||
) -> None: ...
|
||||
def change_drag_thumbnail(os_window_id: int, idx: int = -1) -> None: ...
|
||||
def draw_single_line_of_text(os_window_id: int, text: str, fg: int, bg: int, width: int, padding_y: int = 2) -> bytes: ...
|
||||
def draw_single_line_of_text(os_window_id: int, text: str, fg: int, bg: int, width: int, padding_y: int = 2, max_width: bool = False) -> tuple[bytes, int]: ...
|
||||
def set_tab_being_dragged(tab_id: int = 0, drag_started: bool = False, x: float = 0, y: float = 0) -> None: ...
|
||||
def get_tab_being_dragged() -> tuple[int, bool, float, float]: ...
|
||||
def set_window_being_dragged(window_id: int = 0, drag_started: bool = False, x: float = 0.0, y: float = 0.0) -> None: ...
|
||||
|
|
|
|||
|
|
@ -483,6 +483,77 @@ end:
|
|||
return ok;
|
||||
}
|
||||
|
||||
size_t
|
||||
freetype_text_width_for_single_line(FreeTypeRenderCtx ctx_, const char *text, unsigned sz_px) {
|
||||
RenderCtx *ctx = (RenderCtx*)ctx_;
|
||||
if (!ctx->created) return 0;
|
||||
bool has_text = text && text[0];
|
||||
if (!has_text) return 0;
|
||||
hb_buffer_clear_contents(hb_buffer);
|
||||
if (!hb_buffer_pre_allocate(hb_buffer, 512)) { PyErr_NoMemory(); return 0; }
|
||||
|
||||
size_t text_len = strlen(text);
|
||||
char_type *unicode = calloc(text_len + 1, sizeof(char_type));
|
||||
if (!unicode) { PyErr_NoMemory(); return 0; }
|
||||
text_len = decode_utf8_string(text, text_len, unicode);
|
||||
set_pixel_size(ctx, &main_face, sz_px, true);
|
||||
// Use a very large output_width so nothing is truncated
|
||||
RenderState rs = {
|
||||
.current_face = &main_face, .fg = 0, .bg = 0, .horizontally_center = false,
|
||||
.output_width = SIZE_MAX, .output_height = 0, .stride = 0,
|
||||
.output = NULL, .x = 0, .y = 0, .sz_px = sz_px
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < text_len; i++) {
|
||||
bool add_to_current_buffer = false;
|
||||
char_type codep = unicode[i];
|
||||
char_type next_codep = unicode[i + 1];
|
||||
Face *fallback_font = NULL;
|
||||
if (char_props_for(codep).is_combining_char) {
|
||||
add_to_current_buffer = true;
|
||||
} else if (glyph_id_for_codepoint(&main_face, codep) > 0) {
|
||||
add_to_current_buffer = rs.current_face == &main_face;
|
||||
if (!add_to_current_buffer) fallback_font = &main_face;
|
||||
} else {
|
||||
if (glyph_id_for_codepoint(rs.current_face, codep) > 0) fallback_font = rs.current_face;
|
||||
else fallback_font = find_fallback_font_for(ctx, codep, next_codep);
|
||||
add_to_current_buffer = !fallback_font || rs.current_face == fallback_font;
|
||||
}
|
||||
if (!add_to_current_buffer) {
|
||||
if (rs.pending_in_buffer) {
|
||||
// Shape the current run and accumulate width
|
||||
hb_buffer_guess_segment_properties(hb_buffer);
|
||||
set_pixel_size(ctx, rs.current_face, sz_px, false);
|
||||
hb_shape(rs.current_face->hb, hb_buffer, NULL, 0);
|
||||
unsigned int len = hb_buffer_get_length(hb_buffer);
|
||||
hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(hb_buffer, NULL);
|
||||
for (unsigned int j = 0; j < len; j++) {
|
||||
rs.x += (float)positions[j].x_offset / 64.0f + (float)positions[j].x_advance / 64.0f;
|
||||
}
|
||||
rs.pending_in_buffer = 0;
|
||||
hb_buffer_clear_contents(hb_buffer);
|
||||
}
|
||||
if (fallback_font) rs.current_face = fallback_font;
|
||||
}
|
||||
hb_buffer_add_utf32(hb_buffer, &codep, 1, 0, 1);
|
||||
rs.pending_in_buffer += 1;
|
||||
}
|
||||
if (rs.pending_in_buffer) {
|
||||
hb_buffer_guess_segment_properties(hb_buffer);
|
||||
set_pixel_size(ctx, rs.current_face, sz_px, false);
|
||||
hb_shape(rs.current_face->hb, hb_buffer, NULL, 0);
|
||||
unsigned int len = hb_buffer_get_length(hb_buffer);
|
||||
hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(hb_buffer, NULL);
|
||||
for (unsigned int j = 0; j < len; j++) {
|
||||
rs.x += (float)positions[j].x_offset / 64.0f + (float)positions[j].x_advance / 64.0f;
|
||||
}
|
||||
hb_buffer_clear_contents(hb_buffer);
|
||||
}
|
||||
|
||||
free(unicode);
|
||||
return (size_t)ceilf(rs.x);
|
||||
}
|
||||
|
||||
static uint8_t*
|
||||
render_single_char_bitmap(const FT_Bitmap *bm, size_t *result_width, size_t *result_height) {
|
||||
*result_width = bm->width; *result_height = bm->rows;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ typedef struct {bool created;} *FreeTypeRenderCtx;
|
|||
FreeTypeRenderCtx create_freetype_render_context(const char *family, bool bold, bool italic);
|
||||
void set_main_face_family(FreeTypeRenderCtx ctx, const char *family, bool bold, bool italic);
|
||||
bool render_single_line(FreeTypeRenderCtx ctx, const char *text, unsigned sz_px, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin, bool horizontally_center_runs);
|
||||
size_t freetype_text_width_for_single_line(FreeTypeRenderCtx ctx, const char *text, unsigned sz_px);
|
||||
uint8_t* render_single_ascii_char_as_mask(FreeTypeRenderCtx ctx_, const char ch, size_t *result_width, size_t *result_height);
|
||||
void release_freetype_render_context(FreeTypeRenderCtx ctx);
|
||||
|
||||
|
|
|
|||
31
kitty/glfw.c
31
kitty/glfw.c
|
|
@ -1154,9 +1154,14 @@ apple_url_open_callback(const char* url) {
|
|||
|
||||
|
||||
bool
|
||||
draw_window_title(double font_sz_pts UNUSED, double ydpi UNUSED, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height) {
|
||||
draw_window_title(double font_sz_pts UNUSED, double ydpi UNUSED, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height, size_t *actual_width) {
|
||||
static char buf[2048];
|
||||
strip_csi_(text, buf, arraysz(buf));
|
||||
if (actual_width) {
|
||||
size_t text_w = cocoa_text_width_for_single_line(buf, height);
|
||||
if (text_w > 0 && text_w < width) width = text_w;
|
||||
*actual_width = width;
|
||||
}
|
||||
return cocoa_render_line_of_text(buf, fg, bg, output_buf, width, height);
|
||||
}
|
||||
|
||||
|
|
@ -1204,13 +1209,18 @@ draw_text_callback(GLFWwindow *window, const char *text, uint32_t fg, uint32_t b
|
|||
}
|
||||
|
||||
bool
|
||||
draw_window_title(double font_sz_pts, double ydpi, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height) {
|
||||
draw_window_title(double font_sz_pts, double ydpi, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height, size_t *actual_width) {
|
||||
FreeTypeRenderCtx ctx;
|
||||
if (!(ctx = freetype_render_ctx(false))) return false;
|
||||
static char buf[2048];
|
||||
strip_csi_(text, buf, arraysz(buf));
|
||||
unsigned px_sz = (unsigned)(font_sz_pts * ydpi / 72.);
|
||||
px_sz = MIN(px_sz, 3 * height / 4);
|
||||
if (actual_width) {
|
||||
size_t text_w = freetype_text_width_for_single_line(ctx, buf, px_sz);
|
||||
if (text_w > 0 && text_w < width) width = text_w;
|
||||
*actual_width = width;
|
||||
}
|
||||
#define RGB2BGR(x) (x & 0xFF000000) | ((x & 0xFF0000) >> 16) | (x & 0x00FF00) | ((x & 0x0000FF) << 16)
|
||||
bool ok = render_single_line(ctx, buf, px_sz, RGB2BGR(fg), RGB2BGR(bg), output_buf, width, height, 0, 0, 0, false);
|
||||
#undef RGB2BGR
|
||||
|
|
@ -3077,12 +3087,13 @@ grab_keyboard(PyObject *self UNUSED, PyObject *action) {
|
|||
}
|
||||
|
||||
static PyObject*
|
||||
draw_single_line_of_text(PyObject *self UNUSED, PyObject *args) {
|
||||
draw_single_line_of_text(PyObject *self UNUSED, PyObject *args, PyObject *kw) {
|
||||
unsigned long long os_window_id;
|
||||
const char *text;
|
||||
unsigned int fg, bg;
|
||||
int width, padding_y = 2;
|
||||
if (!PyArg_ParseTuple(args, "KsIIi|i", &os_window_id, &text, &fg, &bg, &width, &padding_y)) return NULL;
|
||||
int width, padding_y = 2, max_width = 0;
|
||||
static const char* kwlist[] = {"os_window_id", "text", "fg", "bg", "width", "padding_y", "max_width", NULL};
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kw, "KsIIi|ip", (char**)kwlist, &os_window_id, &text, &fg, &bg, &width, &padding_y, &max_width)) return NULL;
|
||||
OSWindow *w = os_window_for_id(os_window_id);
|
||||
if (!w || !w->fonts_data) {
|
||||
PyErr_SetString(PyExc_KeyError, "OS Window with specified id does not exist or has no fonts data");
|
||||
|
|
@ -3093,11 +3104,15 @@ draw_single_line_of_text(PyObject *self UNUSED, PyObject *args) {
|
|||
size_t height = (size_t)w->fonts_data->fcm.cell_height + padding_y;
|
||||
size_t buf_sz = (size_t)width * height * 4;
|
||||
RAII_PyObject(ans, PyBytes_FromStringAndSize(NULL, buf_sz)); if (!ans) return NULL;
|
||||
if (!draw_window_title(font_sz_pts, ydpi, text, fg, bg, (uint8_t*)PyBytes_AS_STRING(ans), width, height)) {
|
||||
size_t actual_width = width;
|
||||
if (!draw_window_title(font_sz_pts, ydpi, text, fg, bg, (uint8_t*)PyBytes_AS_STRING(ans), width, height, max_width ? &actual_width : NULL)) {
|
||||
if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError, "Failed to render text");
|
||||
return NULL;
|
||||
}
|
||||
return Py_NewRef(ans);
|
||||
if (actual_width < (size_t)width) {
|
||||
if (_PyBytes_Resize(&ans, actual_width * height * 4) < 0) return NULL;
|
||||
}
|
||||
return Py_BuildValue("Oi", ans, (int)actual_width);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -3242,7 +3257,7 @@ static PyMethodDef module_methods[] = {
|
|||
{"create_os_window", (PyCFunction)(void (*) (void))(create_os_window), METH_VARARGS | METH_KEYWORDS, NULL},
|
||||
{"start_drag_with_data", (PyCFunction)(void (*) (void))(start_drag_with_data), METH_VARARGS | METH_KEYWORDS, NULL},
|
||||
METHODB(change_drag_thumbnail, METH_VARARGS),
|
||||
METHODB(draw_single_line_of_text, METH_VARARGS),
|
||||
{"draw_single_line_of_text", (PyCFunction)(void (*) (void))(draw_single_line_of_text), METH_VARARGS | METH_KEYWORDS, NULL},
|
||||
METHODB(set_default_window_icon, METH_VARARGS),
|
||||
METHODB(set_os_window_icon, METH_VARARGS),
|
||||
METHODB(set_clipboard_data_types, METH_VARARGS),
|
||||
|
|
|
|||
|
|
@ -845,7 +845,7 @@ render_a_bar(const UIRenderData *ui, WindowBarData *bar, PyObject *title, bool a
|
|||
static char titlebuf[2048] = {0};
|
||||
if (!title) return 0;
|
||||
snprintf(titlebuf, arraysz(titlebuf), " %s", PyUnicode_AsUTF8(title));
|
||||
if (!draw_window_title(ui->os_window->fonts_data->font_sz_in_pts, ui->os_window->fonts_data->logical_dpi_y, titlebuf, fg, bg, bar->buf, bar_width, bar_height)) return 0;
|
||||
if (!draw_window_title(ui->os_window->fonts_data->font_sz_in_pts, ui->os_window->fonts_data->logical_dpi_y, titlebuf, fg, bg, bar->buf, bar_width, bar_height, NULL)) return 0;
|
||||
Py_CLEAR(bar->last_drawn_title_object_id);
|
||||
bar->last_drawn_title_object_id = Py_NewRef(title);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -572,7 +572,7 @@ const char* format_mods(unsigned mods);
|
|||
void dispatch_pending_clicks(id_type, void*);
|
||||
void send_pending_click_to_window(Window*, int);
|
||||
void get_platform_dependent_config_values(void *glfw_window);
|
||||
bool draw_window_title(double, double, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height);
|
||||
bool draw_window_title(double, double, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height, size_t *actual_width);
|
||||
uint8_t* draw_single_ascii_char(const char ch, size_t *result_width, size_t *result_height);
|
||||
bool is_os_window_fullscreen(OSWindow *);
|
||||
void update_ime_focus(OSWindow* osw, bool focused);
|
||||
|
|
|
|||
|
|
@ -1762,7 +1762,7 @@ class TabManager: # {{{
|
|||
else:
|
||||
fg = color_as_int(opts.inactive_tab_foreground)
|
||||
bg = color_as_int(opts.inactive_tab_background)
|
||||
title_pixels = draw_single_line_of_text(self.os_window_id, title, 0xff000000 | fg, 0xff000000 | bg, width)
|
||||
title_pixels, width = draw_single_line_of_text(self.os_window_id, title, 0xff000000 | fg, 0xff000000 | bg, width)
|
||||
title_height = len(title_pixels) // (width * 4)
|
||||
thumbnails = ((title_pixels, width, title_height), (title_pixels + pixels, width, title_height + height))
|
||||
drag_data = {
|
||||
|
|
@ -1870,7 +1870,7 @@ class TabManager: # {{{
|
|||
title = str(w.title or '')
|
||||
fg = color_as_int(opts.window_title_bar_active_foreground or opts.active_tab_foreground)
|
||||
bg = color_as_int(opts.window_title_bar_active_background or opts.active_tab_background)
|
||||
title_pixels = draw_single_line_of_text(self.os_window_id, title, 0xff000000 | fg, 0xff000000 | bg, width)
|
||||
title_pixels, width = draw_single_line_of_text(self.os_window_id, title, 0xff000000 | fg, 0xff000000 | bg, width)
|
||||
title_height = len(title_pixels) // (width * 4)
|
||||
thumbnails = ((title_pixels + pixels, width, title_height + height),)
|
||||
drag_data = {f'application/net.kovidgoyal.kitty-window-{os.getpid()}': str(window_id).encode()}
|
||||
|
|
|
|||
|
|
@ -1321,7 +1321,7 @@ class Window:
|
|||
fg = color_as_int(self.screen.color_profile.default_fg)
|
||||
bg = color_as_int(self.screen.color_profile.default_bg)
|
||||
width = self.geometry.right - self.geometry.left
|
||||
pixels = draw_single_line_of_text(self.os_window_id, url, 0xff000000 | fg, 0xff000000 | bg, width)
|
||||
pixels, width = draw_single_line_of_text(self.os_window_id, url, 0xff000000 | fg, 0xff000000 | bg, width, max_width=True)
|
||||
height = len(pixels) // (width * 4)
|
||||
thumbnails = ((pixels, width, height),)
|
||||
drag_data = {'text/uri-list': (url + '\r\n').encode()}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue