diff --git a/Changelog.md b/Changelog.md index fc61188a..5405f3d2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -22,7 +22,15 @@ * OpenGL context ``. * [Documentation](https://nappgui.com/en/ogl3d/ogl3d.html). * [Demo](https://nappgui.com/en/howto/glhello.html). -* `draw_r2df()`, `draw_r2dd()`, `Draw::r2d()`. [Commit](). +* `draw_r2df()`, `draw_r2dd()`, `Draw::r2d()`. [Commit](https://github.com/frang75/nappgui_src/commit/51d4022b5d7535f325b253a29e653b9b88b5ced7). +* `draw2d_preferred_monospace()`. [Commit](). +* `listbox_font()`. [Commit](). +* `ekFCELL` in font style to indicate the font size means cell height. [Commit](). +* Support for font stretch. [Doc](https://nappgui.com/en/draw2d/font.html#h6). [Commit](). + * `font_with_width()`. + * `font_with_xscale()`. + * `font_width()`. + * `font_xscale()`. ### Fixed @@ -32,6 +40,9 @@ * macOS HighSierra and lowers focus ring drawing issue. [Commit](https://github.com/frang75/nappgui_src/commit/a163cb2555101b831414b6deb781a1d1c49ccd42). * Issue in `dbind_destroy()`. [Commit](https://github.com/frang75/nappgui_src/commit/f8e16ad9d7712339400ca55b1a3ab4a426f1da2a). * Issue in `layout_panel_replace()`. [Commit](https://github.com/frang75/nappgui_src/commit/f8e16ad9d7712339400ca55b1a3ab4a426f1da2a). +* GTK buttons always show shortcut underscore. [Commit](). +* macOS pushbuttons images were not shown. [Commit](). +* `gui_alt_color()` avoid to register twice the same color. [Commit](). ### Improved diff --git a/demo/drawhello/drawhello.c b/demo/drawhello/drawhello.c index 2b79b01e..06edcff1 100644 --- a/demo/drawhello/drawhello.c +++ b/demo/drawhello/drawhello.c @@ -12,20 +12,20 @@ struct _app_t Label *label; Cell *slider; uint32_t option; - real32_t gradient; + real32_t slider_pos; }; /*---------------------------------------------------------------------------*/ static void i_draw_lines(DCtx *ctx) { - const V2Df poly1[] = { { 10, 190}, { 90, 110}, {110, 190}, {190, 110}, {210, 190}, {290, 110} }; - const V2Df poly2[] = { {310, 190}, {390, 110}, {410, 190}, {490, 110}, {510, 190}, {590, 110} }; - const V2Df poly3[] = { { 10, 290}, { 90, 210}, {110, 290}, {190, 210}, {210, 290}, {290, 210} }; - const real32_t pattern1[] = { 5, 5, 10, 5 }; - const real32_t pattern2[] = { 1, 1 }; - const real32_t pattern3[] = { 2, 1 }; - const real32_t pattern4[] = { 1, 2 }; + const V2Df poly1[] = {{10, 190}, {90, 110}, {110, 190}, {190, 110}, {210, 190}, {290, 110}}; + const V2Df poly2[] = {{310, 190}, {390, 110}, {410, 190}, {490, 110}, {510, 190}, {590, 110}}; + const V2Df poly3[] = {{10, 290}, {90, 210}, {110, 290}, {190, 210}, {210, 290}, {290, 210}}; + const real32_t pattern1[] = {5, 5, 10, 5}; + const real32_t pattern2[] = {1, 1}; + const real32_t pattern3[] = {2, 1}; + const real32_t pattern4[] = {1, 2}; /* Line widths */ draw_line_color(ctx, kCOLOR_BLACK); @@ -111,15 +111,14 @@ static void i_draw_lines(DCtx *ctx) static void i_draw_shapes_row(DCtx *ctx, const drawop_t op, const T2Df *origin) { - const V2Df poly[] = { {40, 0}, {12.36f, 38.04f}, {-32.36f, 23.52f}, - {-32.36f, -23.52f}, {12.36f, -38.04f} }; + const V2Df poly[] = {{40, 0}, {12.36f, 38.04f}, {-32.36f, 23.52f}, {-32.36f, -23.52f}, {12.36f, -38.04f}}; T2Df matrix; draw_rect(ctx, op, 10, 10, 110, 75); draw_rndrect(ctx, op, 140, 10, 110, 75, 20); draw_circle(ctx, op, 312, 50, 40); draw_ellipse(ctx, op, 430, 50, 55, 37); t2d_movef(&matrix, origin, 547, 50); - t2d_rotatef(&matrix, &matrix, - kBMATH_PIf / 10); + t2d_rotatef(&matrix, &matrix, -kBMATH_PIf / 10); draw_matrixf(ctx, &matrix); draw_polygon(ctx, op, poly, 5); } @@ -190,10 +189,10 @@ static void i_draw_lines_gradient(DCtx *ctx, const real32_t gradient) real32_t stop[2] = {0, 1}; real32_t gpos; real32_t gx, gy; - const real32_t pattern1[] = { 5, 5, 10, 5 }; - const real32_t pattern2[] = { 1, 1 }; - const real32_t pattern3[] = { 2, 1 }; - const real32_t pattern4[] = { 1, 2 }; + const real32_t pattern1[] = {5, 5, 10, 5}; + const real32_t pattern2[] = {1, 1}; + const real32_t pattern3[] = {2, 1}; + const real32_t pattern4[] = {1, 2}; c[0] = kCOLOR_RED; c[1] = kCOLOR_BLUE; @@ -227,9 +226,9 @@ static void i_draw_lines_gradient(DCtx *ctx, const real32_t gradient) draw_bezier(ctx, 30, 250, 140, 110, 440, 170, 570, 250); draw_line_width(ctx, 8); - draw_arc(ctx, 100, 280, 60, 0, - kBMATH_PIf / 2); + draw_arc(ctx, 100, 280, 60, 0, -kBMATH_PIf / 2); draw_arc(ctx, 250, 280, 60, kBMATH_PIf, kBMATH_PIf / 2); - draw_arc(ctx, 300, 220, 60, kBMATH_PIf / 2, - kBMATH_PIf / 2); + draw_arc(ctx, 300, 220, 60, kBMATH_PIf / 2, -kBMATH_PIf / 2); draw_arc(ctx, 450, 220, 60, kBMATH_PIf / 2, kBMATH_PIf / 2); draw_line_width(ctx, 5); @@ -305,7 +304,7 @@ static void i_draw_local_gradient(DCtx *ctx, const real32_t gradient) draw_line(ctx, 0, 0, gx, gy); t2d_movef(&matrix, kT2D_IDENTf, 250, 280); - t2d_rotatef(&matrix, &matrix, - kBMATH_PIf / 10); + t2d_rotatef(&matrix, &matrix, -kBMATH_PIf / 10); draw_matrixf(ctx, &matrix); t2d_movef(&matrix, &matrix, -100, -50); draw_fill_matrix(ctx, &matrix); @@ -338,13 +337,13 @@ static void i_draw_wrap_gradient(DCtx *ctx) /*---------------------------------------------------------------------------*/ -static void i_text_single(DCtx *ctx) +static void i_text_single(DCtx *ctx, const real32_t xscale) { - Font *font = font_system(20, 0); + Font *bfont = font_system(20, 0); + Font *font = font_with_xscale(bfont, xscale); const char_t *text = "Text 文本 Κείμενο"; real32_t width, height; T2Df matrix; - draw_font(ctx, font); draw_text_extents(ctx, text, -1, &width, &height); draw_text_color(ctx, kCOLOR_BLUE); @@ -366,7 +365,6 @@ static void i_text_single(DCtx *ctx) draw_text(ctx, text, 300, 175); draw_text_align(ctx, ekRIGHT, ekBOTTOM); draw_text(ctx, text, 575, 175); - draw_line_color(ctx, kCOLOR_RED); draw_fill_color(ctx, kCOLOR_RED); draw_circle(ctx, ekFILL, 25, 25, 3); @@ -393,57 +391,51 @@ static void i_text_single(DCtx *ctx) draw_rect(ctx, ekSTROKE, 25, 175 - height, width, height); draw_rect(ctx, ekSTROKE, 300 - (width / 2), 175 - height, width, height); draw_rect(ctx, ekSTROKE, 575 - width, 175 - height, width, height); - draw_fill_color(ctx, kCOLOR_BLUE); t2d_movef(&matrix, kT2D_IDENTf, 25, 200); t2d_rotatef(&matrix, &matrix, kBMATH_PIf / 8); draw_matrixf(ctx, &matrix); draw_text_align(ctx, ekLEFT, ekTOP); draw_text(ctx, text, 0, 0); - t2d_movef(&matrix, kT2D_IDENTf, 300, 250); - t2d_rotatef(&matrix, &matrix, - kBMATH_PIf / 8); + t2d_rotatef(&matrix, &matrix, -kBMATH_PIf / 8); draw_matrixf(ctx, &matrix); draw_text_align(ctx, ekCENTER, ekCENTER); draw_text(ctx, text, 0, 0); - t2d_movef(&matrix, kT2D_IDENTf, 25, 325); t2d_scalef(&matrix, &matrix, 3, 1); draw_matrixf(ctx, &matrix); draw_text_align(ctx, ekLEFT, ekTOP); draw_text(ctx, text, 0, 0); - t2d_movef(&matrix, kT2D_IDENTf, 575, 200); t2d_scalef(&matrix, &matrix, .5f, 1); draw_matrixf(ctx, &matrix); draw_text_align(ctx, ekRIGHT, ekTOP); draw_text(ctx, text, 0, 0); - t2d_movef(&matrix, kT2D_IDENTf, 575, 230); t2d_scalef(&matrix, &matrix, .75f, 1); draw_matrixf(ctx, &matrix); draw_text_align(ctx, ekRIGHT, ekTOP); draw_text(ctx, text, 0, 0); - t2d_movef(&matrix, kT2D_IDENTf, 575, 260); t2d_scalef(&matrix, &matrix, 1.25f, 1); draw_matrixf(ctx, &matrix); draw_text_align(ctx, ekRIGHT, ekTOP); draw_text(ctx, text, 0, 0); - + font_destroy(&bfont); font_destroy(&font); } /*---------------------------------------------------------------------------*/ -static void i_text_newline(DCtx *ctx) +static void i_text_newline(DCtx *ctx, const real32_t xscale) { - Font *font = font_system(20, 0); + Font *bfont = font_system(20, 0); + Font *font = font_with_xscale(bfont, xscale); const char_t *text = "Text new line\n文字换行\nΓραμμή κειμένου"; real32_t width, height; draw_font(ctx, font); draw_text_extents(ctx, text, -1, &width, &height); - draw_text_color(ctx, kCOLOR_BLUE); draw_text_align(ctx, ekLEFT, ekTOP); draw_text_halign(ctx, ekLEFT); @@ -451,7 +443,6 @@ static void i_text_newline(DCtx *ctx) draw_text_align(ctx, ekCENTER, ekTOP); draw_text_halign(ctx, ekCENTER); draw_text(ctx, text, 300, 25); - draw_text_align(ctx, ekRIGHT, ekTOP); draw_text_halign(ctx, ekRIGHT); draw_text(ctx, text, 575, 25); @@ -473,7 +464,6 @@ static void i_text_newline(DCtx *ctx) draw_text_align(ctx, ekRIGHT, ekBOTTOM); draw_text_halign(ctx, ekRIGHT); draw_text(ctx, text, 575, 325); - draw_line_color(ctx, kCOLOR_RED); draw_fill_color(ctx, kCOLOR_RED); draw_circle(ctx, ekFILL, 25, 25, 3); @@ -494,20 +484,23 @@ static void i_text_newline(DCtx *ctx) draw_rect(ctx, ekSTROKE, 25, 325 - height, width, height); draw_rect(ctx, ekSTROKE, 300 - (width / 2), 325 - height, width, height); draw_rect(ctx, ekSTROKE, 575 - width, 325 - height, width, height); + font_destroy(&bfont); font_destroy(&font); } /*---------------------------------------------------------------------------*/ -static void i_text_block(DCtx *ctx) +static void i_text_block(DCtx *ctx, const real32_t xscale) { const char_t *text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."; + Font *bfont = font_system(font_regular_size(), 0); + Font *font = font_with_xscale(bfont, xscale); real32_t dash[2] = {1, 1}; real32_t width1, height1; real32_t width2, height2; real32_t width3, height3; real32_t width4, height4; - + draw_font(ctx, font); draw_text_color(ctx, kCOLOR_BLUE); draw_text_align(ctx, ekLEFT, ekTOP); draw_text_halign(ctx, ekLEFT); @@ -523,7 +516,6 @@ static void i_text_block(DCtx *ctx) draw_text_width(ctx, 500); draw_text_extents(ctx, text, 500, &width4, &height4); draw_text(ctx, text, 25, 315); - draw_line_color(ctx, kCOLOR_RED); draw_fill_color(ctx, kCOLOR_RED); draw_circle(ctx, ekFILL, 25, 25, 3); @@ -533,12 +525,14 @@ static void i_text_block(DCtx *ctx) draw_rect(ctx, ekSTROKE, 25, 25, 200, height1); draw_rect(ctx, ekSTROKE, 250, 25, 300, height2); draw_rect(ctx, ekSTROKE, 25, 200, 400, height3); - draw_rect(ctx, ekSTROKE, 25, 315, 500, height4); + draw_rect(ctx, ekSTROKE, 25, 315, 500, height4); draw_line_dash(ctx, dash, 2); draw_rect(ctx, ekSTROKE, 25, 25, width1, height1); draw_rect(ctx, ekSTROKE, 250, 25, width2, height2); draw_rect(ctx, ekSTROKE, 25, 200, width3, height3); draw_rect(ctx, ekSTROKE, 25, 315, width4, height4); + font_destroy(&bfont); + font_destroy(&font); } /*---------------------------------------------------------------------------*/ @@ -613,7 +607,8 @@ static void i_OnDraw(App *app, Event *e) { const EvDraw *p = event_params(e, EvDraw); draw_clear(p->ctx, color_rgb(200, 200, 200)); - switch (app->option) { + switch (app->option) + { case 0: cell_enabled(app->slider, FALSE); label_text(app->label, "Different line styles: width, join, cap, dash..."); @@ -628,27 +623,27 @@ static void i_OnDraw(App *app, Event *e) case 2: cell_enabled(app->slider, TRUE); label_text(app->label, "Global linear gradient."); - i_draw_gradient(p->ctx, app->gradient, TRUE, FALSE); + i_draw_gradient(p->ctx, app->slider_pos, TRUE, FALSE); break; case 3: cell_enabled(app->slider, TRUE); label_text(app->label, "Shapes filled with global (identity) linear gradient."); - i_draw_gradient(p->ctx, app->gradient, TRUE, TRUE); + i_draw_gradient(p->ctx, app->slider_pos, TRUE, TRUE); break; case 4: cell_enabled(app->slider, TRUE); label_text(app->label, "Shapes filled with global (identity) linear gradient."); - i_draw_gradient(p->ctx, app->gradient, FALSE, TRUE); + i_draw_gradient(p->ctx, app->slider_pos, FALSE, TRUE); break; case 5: cell_enabled(app->slider, TRUE); label_text(app->label, "Lines with global (identity) linear gradient."); - i_draw_lines_gradient(p->ctx, app->gradient); + i_draw_lines_gradient(p->ctx, app->slider_pos); break; case 6: cell_enabled(app->slider, TRUE); label_text(app->label, "Shapes filled with local (transformed) gradient."); - i_draw_local_gradient(p->ctx, app->gradient); + i_draw_local_gradient(p->ctx, app->slider_pos); break; case 7: cell_enabled(app->slider, FALSE); @@ -656,19 +651,19 @@ static void i_OnDraw(App *app, Event *e) i_draw_wrap_gradient(p->ctx); break; case 8: - cell_enabled(app->slider, FALSE); + cell_enabled(app->slider, TRUE); label_text(app->label, "Single line text with alignment and transforms"); - i_text_single(p->ctx); + i_text_single(p->ctx, app->slider_pos + .5f); break; case 9: - cell_enabled(app->slider, FALSE); + cell_enabled(app->slider, TRUE); label_text(app->label, "Text with newline '\\n' character and internal alignment"); - i_text_newline(p->ctx); + i_text_newline(p->ctx, app->slider_pos + .5f); break; case 10: - cell_enabled(app->slider, FALSE); + cell_enabled(app->slider, TRUE); label_text(app->label, "Text block in a constrained width area"); - i_text_block(p->ctx); + i_text_block(p->ctx, app->slider_pos + .5f); break; case 11: cell_enabled(app->slider, FALSE); @@ -697,7 +692,10 @@ static void i_OnAcceptFocus(App *app, Event *e) static void i_OnSelect(App *app, Event *e) { const EvButton *p = event_params(e, EvButton); + Slider *slider = cell_slider(app->slider); app->option = p->index; + app->slider_pos = 0.5f; + slider_value(slider, app->slider_pos); view_update(app->view); } @@ -706,7 +704,7 @@ static void i_OnSelect(App *app, Event *e) static void i_OnSlider(App *app, Event *e) { const EvSlider *p = event_params(e, EvSlider); - app->gradient = p->pos; + app->slider_pos = p->pos; view_update(app->view); } @@ -724,7 +722,7 @@ static Panel *i_panel(App *app) Slider *slider = slider_create(); View *view = view_create(); label_text(label1, "Select primitives:"); - label_text(label2, "Gradient angle"); + label_text(label2, "Gradient/scale"); popup_add_elem(popup, "Lines", NULL); popup_add_elem(popup, "Shapes", NULL); popup_add_elem(popup, "Gradient-1", NULL); @@ -782,7 +780,7 @@ static App *i_create(void) App *app = heap_new0(App); Panel *panel = i_panel(app); app->window = window_create(ekWINDOW_STD); - app->gradient = 0; + app->slider_pos = 0; app->option = 0; window_panel(app->window, panel); window_title(app->window, "Drawing primitives"); diff --git a/demo/guihello/fontx.c b/demo/guihello/fontx.c new file mode 100644 index 00000000..b5a8787a --- /dev/null +++ b/demo/guihello/fontx.c @@ -0,0 +1,300 @@ +/* Font x-scale */ + +#include "fontx.h" +#include + +typedef struct _fontx_t FontX; + +struct _fontx_t +{ + Font *font_1_0; + Font *font_1_5; + Font *font_0_5; + Font *fontm_1_0; + Font *fontm_1_5; + Font *fontm_0_5; + Label *mline; + Layout *layout; + color_t c1; + color_t c2; +}; + +const char_t *i_TEXT1 = "Font system regular"; +const char_t *i_TEXT2 = "Font monospace regular"; +const char_t *i_TEXT3 = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."; + +/*---------------------------------------------------------------------------*/ + +static void i_destroy_fontx(FontX **fontx) +{ + cassert_no_null(fontx); + cassert_no_null(*fontx); + font_destroy(&(*fontx)->font_1_0); + font_destroy(&(*fontx)->font_1_5); + font_destroy(&(*fontx)->font_0_5); + font_destroy(&(*fontx)->fontm_1_0); + font_destroy(&(*fontx)->fontm_1_5); + font_destroy(&(*fontx)->fontm_0_5); + heap_delete(fontx, FontX); +} + +/*---------------------------------------------------------------------------*/ + +static void i_OnMoved(FontX *fontx, Event *e) +{ + const EvSlider *p = event_params(e, EvSlider); + Font *font = font_system(font_regular_size(), 0); + Font *font_sx = font_with_xscale(font, p->pos + .5f); + label_font(fontx->mline, font_sx); + layout_update(fontx->layout); + font_destroy(&font_sx); + font_destroy(&font); +} + +/*---------------------------------------------------------------------------*/ + +static Layout *i_label_layout(FontX *fontx) +{ + Layout *layout1 = layout_create(2, 1); + Layout *layout2 = layout_create(1, 7); + Slider *slider = slider_create(); + Label *label1 = label_create(); + Label *label2 = label_create(); + Label *label3 = label_create(); + Label *label4 = label_create(); + Label *label5 = label_create(); + Label *label6 = label_create(); + Label *label7 = label_multiline(); + cassert_no_null(fontx); + slider_value(slider, .5f); + slider_OnMoved(slider, listener(fontx, i_OnMoved, FontX)); + label_text(label1, i_TEXT1); + label_text(label2, i_TEXT1); + label_text(label3, i_TEXT1); + label_text(label4, i_TEXT2); + label_text(label5, i_TEXT2); + label_text(label6, i_TEXT2); + label_text(label7, i_TEXT3); + label_font(label1, fontx->font_1_0); + label_font(label2, fontx->font_1_5); + label_font(label3, fontx->font_0_5); + label_font(label4, fontx->fontm_1_0); + label_font(label5, fontx->fontm_1_5); + label_font(label6, fontx->fontm_0_5); + label_font(label7, fontx->font_1_0); + label_bgcolor(label1, fontx->c1); + label_bgcolor(label2, fontx->c1); + label_bgcolor(label3, fontx->c1); + label_bgcolor(label4, fontx->c1); + label_bgcolor(label5, fontx->c1); + label_bgcolor(label6, fontx->c1); + label_bgcolor(label7, fontx->c1); + layout_slider(layout2, slider, 0, 0); + layout_label(layout2, label1, 0, 1); + layout_label(layout2, label2, 0, 2); + layout_label(layout2, label3, 0, 3); + layout_label(layout2, label4, 0, 4); + layout_label(layout2, label5, 0, 5); + layout_label(layout2, label6, 0, 6); + layout_vmargin(layout2, 0, 5); + layout_valign(layout1, 0, 0, ekTOP); + layout_valign(layout1, 1, 0, ekTOP); + layout_hsize(layout1, 1, 300); + layout_layout(layout1, layout2, 0, 0); + layout_label(layout1, label7, 1, 0); + fontx->mline = label7; + fontx->layout = layout1; + return layout1; +} + +/*---------------------------------------------------------------------------*/ + +static Layout *i_button_layout(FontX *fontx) +{ + Layout *layout = layout_create(1, 6); + Button *button1 = button_push(); + Button *button2 = button_push(); + Button *button3 = button_push(); + Button *button4 = button_push(); + Button *button5 = button_push(); + Button *button6 = button_push(); + cassert_no_null(fontx); + button_text(button1, i_TEXT1); + button_text(button2, i_TEXT1); + button_text(button3, i_TEXT1); + button_text(button4, i_TEXT2); + button_text(button5, i_TEXT2); + button_text(button6, i_TEXT2); + button_font(button1, fontx->font_1_0); + button_font(button2, fontx->font_1_5); + button_font(button3, fontx->font_0_5); + button_font(button4, fontx->fontm_1_0); + button_font(button5, fontx->fontm_1_5); + button_font(button6, fontx->fontm_0_5); + layout_button(layout, button1, 0, 0); + layout_button(layout, button2, 0, 1); + layout_button(layout, button3, 0, 2); + layout_button(layout, button4, 0, 3); + layout_button(layout, button5, 0, 4); + layout_button(layout, button6, 0, 5); + layout_halign(layout, 0, 0, ekLEFT); + layout_halign(layout, 0, 1, ekLEFT); + layout_halign(layout, 0, 2, ekLEFT); + layout_halign(layout, 0, 3, ekLEFT); + layout_halign(layout, 0, 4, ekLEFT); + layout_halign(layout, 0, 5, ekLEFT); + return layout; +} + +/*---------------------------------------------------------------------------*/ + +static Layout *i_edit_layout(FontX *fontx) +{ + Layout *layout = layout_create(1, 6); + Edit *edit1 = edit_create(); + Edit *edit2 = edit_create(); + Edit *edit3 = edit_create(); + Edit *edit4 = edit_create(); + Edit *edit5 = edit_create(); + Edit *edit6 = edit_create(); + cassert_no_null(fontx); + edit_text(edit1, i_TEXT1); + edit_text(edit2, i_TEXT1); + edit_text(edit3, i_TEXT1); + edit_text(edit4, i_TEXT2); + edit_text(edit5, i_TEXT2); + edit_text(edit6, i_TEXT2); + edit_font(edit1, fontx->font_1_0); + edit_font(edit2, fontx->font_1_5); + edit_font(edit3, fontx->font_0_5); + edit_font(edit4, fontx->fontm_1_0); + edit_font(edit5, fontx->fontm_1_5); + edit_font(edit6, fontx->fontm_0_5); + layout_edit(layout, edit1, 0, 0); + layout_edit(layout, edit2, 0, 1); + layout_edit(layout, edit3, 0, 2); + layout_edit(layout, edit4, 0, 3); + layout_edit(layout, edit5, 0, 4); + layout_edit(layout, edit6, 0, 5); + return layout; +} + +/*---------------------------------------------------------------------------*/ + +static ListBox *i_listbox(const Font *font, const char_t *text) +{ + ListBox *listbox = listbox_create(); + listbox_font(listbox, font); + listbox_add_elem(listbox, text, NULL); + listbox_size(listbox, s2df(100, 50)); + return listbox; +} + +/*---------------------------------------------------------------------------*/ + +static Layout *i_list_layout(FontX *fontx) +{ + Layout *layout = layout_create(6, 1); + ListBox *list1 = i_listbox(fontx->font_1_0, i_TEXT1); + ListBox *list2 = i_listbox(fontx->font_1_5, i_TEXT1); + ListBox *list3 = i_listbox(fontx->font_0_5, i_TEXT1); + ListBox *list4 = i_listbox(fontx->fontm_1_0, i_TEXT2); + ListBox *list5 = i_listbox(fontx->fontm_1_5, i_TEXT2); + ListBox *list6 = i_listbox(fontx->fontm_0_5, i_TEXT2); + layout_listbox(layout, list1, 0, 0); + layout_listbox(layout, list2, 1, 0); + layout_listbox(layout, list3, 2, 0); + layout_listbox(layout, list4, 3, 0); + layout_listbox(layout, list5, 4, 0); + layout_listbox(layout, list6, 5, 0); + return layout; +} + +/*---------------------------------------------------------------------------*/ + +static void i_OnDraw(FontX *fontx, Event *e) +{ + const EvDraw *p = event_params(e, EvDraw); + real32_t w1, w2, w3, w4, w5, w6; + real32_t h1, h2, h3, h4, h5, h6; + cassert_no_null(fontx); + draw_clear(p->ctx, fontx->c2); + draw_text_color(p->ctx, gui_label_color()); + draw_font(p->ctx, fontx->font_1_0); + draw_text_extents(p->ctx, i_TEXT1, -1, &w1, &h1); + draw_text(p->ctx, i_TEXT1, 0, 0); + draw_font(p->ctx, fontx->font_1_5); + draw_text_extents(p->ctx, i_TEXT1, -1, &w2, &h2); + draw_text(p->ctx, i_TEXT1, 0, h1); + draw_font(p->ctx, fontx->font_0_5); + draw_text_extents(p->ctx, i_TEXT1, -1, &w3, &h3); + draw_text(p->ctx, i_TEXT1, 0, h1 + h2); + draw_font(p->ctx, fontx->fontm_1_0); + draw_text_extents(p->ctx, i_TEXT2, -1, &w4, &h4); + draw_text(p->ctx, i_TEXT2, 0, h1 + h2 + h3); + draw_font(p->ctx, fontx->fontm_1_5); + draw_text_extents(p->ctx, i_TEXT2, -1, &w5, &h5); + draw_text(p->ctx, i_TEXT2, 0, h1 + h2 + h3 + h4); + draw_font(p->ctx, fontx->fontm_0_5); + draw_text_extents(p->ctx, i_TEXT2, -1, &w6, &h6); + draw_text(p->ctx, i_TEXT2, 0, h1 + h2 + h3 + h4 + h5); + draw_line_color(p->ctx, kCOLOR_RED); + draw_rect(p->ctx, ekSTROKE, 0, 0, w1, h1); + draw_rect(p->ctx, ekSTROKE, 0, h1, w2, h2); + draw_rect(p->ctx, ekSTROKE, 0, h1 + h2, w3, h3); + draw_rect(p->ctx, ekSTROKE, 0, h1 + h2 + h3, w4, h4); + draw_rect(p->ctx, ekSTROKE, 0, h1 + h2 + h3 + h4, w5, h5); + draw_rect(p->ctx, ekSTROKE, 0, h1 + h2 + h3 + h4 + h5, w6, h6); +} + +/*---------------------------------------------------------------------------*/ + +static Layout *i_layout(FontX *fontx) +{ + Layout *layout1 = layout_create(1, 5); + Layout *layout2 = i_label_layout(fontx); + Layout *layout3 = i_button_layout(fontx); + Layout *layout4 = i_edit_layout(fontx); + Layout *layout5 = i_list_layout(fontx); + View *view = view_create(); + view_OnDraw(view, listener(fontx, i_OnDraw, FontX)); + view_size(view, s2df(200, 120)); + layout_layout(layout1, layout2, 0, 0); + layout_view(layout1, view, 0, 1); + layout_layout(layout1, layout3, 0, 2); + layout_layout(layout1, layout4, 0, 3); + layout_layout(layout1, layout5, 0, 4); + layout_vmargin(layout1, 0, 5); + layout_vmargin(layout1, 1, 5); + layout_vmargin(layout1, 2, 5); + layout_vmargin(layout1, 3, 5); + return layout1; +} + +/*---------------------------------------------------------------------------*/ + +static FontX *i_fontx(void) +{ + FontX *fontx = heap_new(FontX); + fontx->font_1_0 = font_system(font_regular_size(), 0); + fontx->font_1_5 = font_with_xscale(fontx->font_1_0, 1.5f); + fontx->font_0_5 = font_with_xscale(fontx->font_1_0, 0.5f); + fontx->fontm_1_0 = font_monospace(font_regular_size(), 0); + fontx->fontm_1_5 = font_with_xscale(fontx->fontm_1_0, 1.5f); + fontx->fontm_0_5 = font_with_xscale(fontx->fontm_1_0, 0.5f); + fontx->c1 = gui_alt_color(color_rgb(192, 255, 255), color_rgb(48, 112, 112)); + fontx->c2 = gui_alt_color(color_rgb(255, 192, 255), color_rgb(128, 48, 112)); + return fontx; +} + +/*---------------------------------------------------------------------------*/ + +Panel *font_x_scale(void) +{ + FontX *fontx = i_fontx(); + Layout *layout = i_layout(fontx); + Panel *panel = panel_create(); + panel_data(panel, &fontx, i_destroy_fontx, FontX); + panel_layout(panel, layout); + return panel; +} diff --git a/demo/guihello/fontx.h b/demo/guihello/fontx.h new file mode 100644 index 00000000..cc6f0da0 --- /dev/null +++ b/demo/guihello/fontx.h @@ -0,0 +1,5 @@ +/* Font x-scale */ + +#include + +Panel *font_x_scale(void); diff --git a/demo/guihello/form.c b/demo/guihello/form.c index e63d7e3d..2af0e80f 100644 --- a/demo/guihello/form.c +++ b/demo/guihello/form.c @@ -229,7 +229,7 @@ static Layout *i_numbers(FormData *data, color_t colorbg) static Layout *i_edits(FormData *data) { - color_t colorbg = gui_alt_color(color_bgr(0xFFFFe4), color_bgr(0x101010)); + color_t colorbg = gui_alt_color(color_rgb(255, 255, 192), color_rgb(112, 112, 48)); Layout *layout1 = layout_create(2, 6); Layout *layout2 = i_numbers(data, colorbg); Label *label1 = label_create(); diff --git a/demo/guihello/guihello.c b/demo/guihello/guihello.c index 5e41ee4c..a4c61180 100644 --- a/demo/guihello/guihello.c +++ b/demo/guihello/guihello.c @@ -5,6 +5,7 @@ #include "buttons.h" #include "sliders.h" #include "editor.h" +#include "fontx.h" #include "form.h" #include "seltext.h" #include "popcom.h" @@ -125,6 +126,9 @@ static void i_set_panel(App *app, const uint32_t index) case 26: panel = ip_input(app->window); break; + case 27: + panel = font_x_scale(); + break; } layout_panel_replace(app->layout, panel, 1, 0); @@ -181,6 +185,7 @@ static Panel *i_panel(App *app) listbox_add_elem(list, "Scroll panel", NULL); listbox_add_elem(list, "Dynamic layouts", NULL); listbox_add_elem(list, "IP Input", NULL); + listbox_add_elem(list, "Font x-scale", NULL); listbox_select(list, 0, TRUE); listbox_OnSelect(list, listener(app, i_OnSelect, App)); layout_listbox(layout, list, 0, 0); diff --git a/demo/guihello/vpadding.c b/demo/guihello/vpadding.c index 178ddbbd..cc1ae6e9 100644 --- a/demo/guihello/vpadding.c +++ b/demo/guihello/vpadding.c @@ -17,13 +17,13 @@ Panel *vpadding(void) Button *button2 = button_push(); Button *button3 = button_push(); Panel *panel = panel_create(); + color_t bg = gui_alt_color(color_rgb(192, 255, 255), color_rgb(48, 112, 112)); edit_text(edit1, "Edit with default padding"); - /* edit_font(edit1, font); */ edit_text(edit2, "Edit with zero padding"); edit_text(edit3, "Edit with high padding"); - edit_bgcolor(edit1, color_rgb(120, 255, 255)); - edit_bgcolor(edit2, color_rgb(120, 255, 255)); - edit_bgcolor(edit3, color_rgb(120, 255, 255)); + edit_bgcolor(edit1, bg); + edit_bgcolor(edit2, bg); + edit_bgcolor(edit3, bg); button_text(button1, "Button with default padding"); button_text(button2, "Button with zero padding"); button_text(button3, "Button with high padding"); diff --git a/prj/NAppClang.cmake b/prj/NAppClang.cmake index 262ae2eb..9acc09fc 100644 --- a/prj/NAppClang.cmake +++ b/prj/NAppClang.cmake @@ -6,7 +6,7 @@ macro(nap_clang_flags arch) # Warnings - set(FLAGS "-Wall -Wextra -pedantic -fPIE -Wno-long-long -Wno-overlength-strings -Wno-newline-eof -Wno-undefined-var-template") + set(FLAGS "-Wall -Wextra -pedantic -fPIE -Wimplicit-function-declaration -Wno-long-long -Wno-overlength-strings -Wno-newline-eof -Wno-undefined-var-template") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}") diff --git a/prj/build.txt b/prj/build.txt index ec296f1c..04383b66 100644 --- a/prj/build.txt +++ b/prj/build.txt @@ -1 +1 @@ -5458 +5533 diff --git a/src/draw2d/draw2d.c b/src/draw2d/draw2d.c index 8f889c3d..c4f5fd09 100644 --- a/src/draw2d/draw2d.c +++ b/src/draw2d/draw2d.c @@ -48,7 +48,10 @@ DeclSt(IColor); static uint32_t i_NUM_USERS = 0; static ArrPt(String) *i_FONT_FAMILIES; static ArrSt(IColor) *i_INDEXED_COLORS; +static String *i_USER_MONOSPACE_FONT_FAMILY = NULL; static String *i_MONOSPACE_FONT_FAMILY = NULL; +static const char_t *i_AVG_CHAR_WIDTH = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; +static uint32_t i_AVG_CHAR_WIDTH_LEN = 0; /*---------------------------------------------------------------------------*/ @@ -83,6 +86,7 @@ void draw2d_start(void) } i_INDEXED_COLORS = arrst_create(IColor); + i_AVG_CHAR_WIDTH_LEN = str_len_c(i_AVG_CHAR_WIDTH); dbind_opaque(Image, image_from_data, NULL, image_copy, NULL, image_write, image_destroy); } @@ -100,6 +104,7 @@ void draw2d_finish(void) dbind_opaque_destroy("Image"); arrpt_destroy(&i_FONT_FAMILIES, str_destroy, String); arrst_destroy(&i_INDEXED_COLORS, NULL, IColor); + str_destopt(&i_USER_MONOSPACE_FONT_FAMILY); str_destopt(&i_MONOSPACE_FONT_FAMILY); osfont_dealloc_globals(); osimage_dealloc_globals(); @@ -112,6 +117,14 @@ void draw2d_finish(void) /*---------------------------------------------------------------------------*/ +void draw2d_preferred_monospace(const char_t *family) +{ + str_upd(&i_USER_MONOSPACE_FONT_FAMILY, family); + str_destopt(&i_MONOSPACE_FONT_FAMILY); +} + +/*---------------------------------------------------------------------------*/ + uint32_t draw2d_register_font(const char_t *font_family) { /* Check if font name is a system font */ @@ -401,6 +414,13 @@ const char_t *draw2d_monospace_family(const char_t **desired_fonts, const uint32 { uint32_t i = 0; ArrPt(String) *installed_fonts = font_installed_families(); + + if (str_empty(i_USER_MONOSPACE_FONT_FAMILY) == FALSE) + { + if (arrpt_bsearch_const(installed_fonts, str_cmp, tc(i_USER_MONOSPACE_FONT_FAMILY), NULL, String, char_t) != NULL) + i_MONOSPACE_FONT_FAMILY = str_copy(i_USER_MONOSPACE_FONT_FAMILY); + } + for (i = 0; i < n && i_MONOSPACE_FONT_FAMILY == NULL; ++i) { if (arrpt_bsearch_const(installed_fonts, str_cmp, desired_fonts[i], NULL, String, char_t) != NULL) @@ -416,3 +436,22 @@ const char_t *draw2d_monospace_family(const char_t **desired_fonts, const uint32 return tc(i_MONOSPACE_FONT_FAMILY); } + +/*---------------------------------------------------------------------------*/ + +const char_t *draw2d_get_preferred_monospace(void) +{ + if (i_USER_MONOSPACE_FONT_FAMILY != NULL) + return tc(i_USER_MONOSPACE_FONT_FAMILY); + else + return NULL; +} + +/*---------------------------------------------------------------------------*/ + +const char_t *draw2d_str_avg_char_width(uint32_t *len) +{ + cassert_no_null(len); + *len = i_AVG_CHAR_WIDTH_LEN; + return i_AVG_CHAR_WIDTH; +} \ No newline at end of file diff --git a/src/draw2d/draw2d.h b/src/draw2d/draw2d.h index a632a157..603285c7 100644 --- a/src/draw2d/draw2d.h +++ b/src/draw2d/draw2d.h @@ -19,4 +19,6 @@ _draw2d_api void draw2d_start(void); _draw2d_api void draw2d_finish(void); +_draw2d_api void draw2d_preferred_monospace(const char_t *family); + __END_C diff --git a/src/draw2d/draw2d.hxx b/src/draw2d/draw2d.hxx index 7afb8518..7d922414 100644 --- a/src/draw2d/draw2d.hxx +++ b/src/draw2d/draw2d.hxx @@ -49,7 +49,8 @@ typedef enum _fstyle_t ekFSUPSCRIPT = 32, ekFPIXELS = 0, - ekFPOINTS = 64 + ekFPOINTS = 64, + ekFCELL = 128 } fstyle_t; typedef enum _linecap_t @@ -116,4 +117,4 @@ typedef struct _font_t Font; DeclSt(color_t); DeclPt(Image); -#endif +#endif \ No newline at end of file diff --git a/src/draw2d/draw2d.inl b/src/draw2d/draw2d.inl index 1e81bc5f..a6d2a857 100644 --- a/src/draw2d/draw2d.inl +++ b/src/draw2d/draw2d.inl @@ -24,6 +24,10 @@ void draw2d_extents_imp(void *data, FPtr_word_extents func_word_extents, const b const char_t *draw2d_monospace_family(const char_t **desired_fonts, const uint32_t n); +const char_t *draw2d_get_preferred_monospace(void); + +const char_t *draw2d_str_avg_char_width(uint32_t *len); + __END_C #define draw2d_extents(data, func_word_extents, newlines, str, refwidth, width, height, type) \ diff --git a/src/draw2d/font.c b/src/draw2d/font.c index 9c5ab46f..7881e45b 100644 --- a/src/draw2d/font.c +++ b/src/draw2d/font.c @@ -15,6 +15,7 @@ #include "draw2d.inl" #include #include +#include #include #include @@ -24,10 +25,12 @@ struct _font_t uint32_t family; uint32_t style; real32_t size; + real32_t xscale; real32_t cell_size; real32_t leading; real32_t ascent; real32_t descent; + real32_t avg_width; bool_t monospace; bool_t metrics; String *family_name; @@ -47,10 +50,12 @@ static Font *i_create_font(const uint32_t family, const real32_t size, const uin font->family = family; font->size = size; font->style = style; + font->xscale = 1; font->cell_size = -1; font->leading = -1; font->ascent = -1; font->descent = -1; + font->avg_width = -1; font->monospace = FALSE; font->metrics = FALSE; font->family_name = NULL; @@ -60,6 +65,31 @@ static Font *i_create_font(const uint32_t family, const real32_t size, const uin /*---------------------------------------------------------------------------*/ +static ___INLINE void i_osfont(Font *font) +{ + cassert_no_null(font); + if (font->osfont == NULL) + { + const char_t *fname = draw2d_font_family(font->family); + font->osfont = osfont_create(fname, font->size, -1, -1, font->style); + } +} + +/*---------------------------------------------------------------------------*/ + +static ___INLINE void i_metrics(Font *font) +{ + cassert_no_null(font); + if (font->metrics == FALSE) + { + i_osfont(font); + osfont_metrics(font->osfont, font->size, font->xscale, &font->ascent, &font->descent, &font->leading, &font->cell_size, &font->avg_width, &font->monospace); + font->metrics = TRUE; + } +} + +/*---------------------------------------------------------------------------*/ + void font_destroy(Font **font) { cassert_no_null(font); @@ -110,6 +140,66 @@ Font *font_with_style(const Font *font, const uint32_t style) /*---------------------------------------------------------------------------*/ +Font *font_with_width(const Font *font, const real32_t width) +{ + Font *font_nscaled = cast(font, Font); + Font *font_scaled = NULL; + const char_t *fname = NULL; + cassert_no_null(font); + cassert(width > 0); + + /* The incomming font is scaled */ + if (bmath_absf(font_nscaled->xscale - 1.f) > 0.01f) + font_nscaled = i_create_font(font->family, font->size, font->style); + + i_metrics(font_nscaled); + cassert(font_nscaled->avg_width > 0); + + font_scaled = i_create_font(font_nscaled->family, font_nscaled->size, font_nscaled->style); + font_scaled->xscale = width / font_nscaled->avg_width; + cassert(font_scaled->osfont == NULL); + fname = draw2d_font_family(font_scaled->family); + font_scaled->osfont = osfont_create(fname, font_scaled->size, width, font_scaled->xscale, font_scaled->style); + + if (font_nscaled != font) + font_destroy(&font_nscaled); + + return font_scaled; +} + +/*---------------------------------------------------------------------------*/ + +Font *font_with_xscale(const Font *font, const real32_t scale) +{ + Font *font_nscaled = cast(font, Font); + Font *font_scaled = NULL; + const char_t *fname = NULL; + real32_t width = 0; + cassert_no_null(font); + cassert(scale > 0); + + /* The incomming font is scaled */ + if (bmath_absf(font_nscaled->xscale - 1.f) > 0.01f) + font_nscaled = i_create_font(font->family, font->size, font->style); + + i_metrics(font_nscaled); + cassert(font_nscaled->avg_width > 0); + + font_scaled = i_create_font(font_nscaled->family, font_nscaled->size, font_nscaled->style); + font_scaled->xscale = scale; + width = font_nscaled->avg_width * scale; + cassert(font_scaled->osfont == NULL); + fname = draw2d_font_family(font_scaled->family); + font_scaled->osfont = osfont_create(fname, font_scaled->size, width, font_scaled->xscale, font_scaled->style); + + if (font_nscaled != font) + font_destroy(&font_nscaled); + + return font_scaled; +} + +/*---------------------------------------------------------------------------*/ + Font *font_copy(const Font *font) { cassert_no_null(font); @@ -127,6 +217,8 @@ bool_t font_equals(const Font *font1, const Font *font2) return FALSE; if (i_abs(font1->size - font2->size) > 0.0001f) return FALSE; + if (i_abs(font1->xscale - font2->xscale) > 0.01f) + return FALSE; if (font1->style != font2->style) return FALSE; return TRUE; @@ -134,18 +226,6 @@ bool_t font_equals(const Font *font1, const Font *font2) /*---------------------------------------------------------------------------*/ -static ___INLINE void i_osfont(Font *font) -{ - cassert_no_null(font); - if (font->osfont == NULL) - { - const char_t *fname = draw2d_font_family(font->family); - font->osfont = osfont_create(fname, font->size, font->style); - } -} - -/*---------------------------------------------------------------------------*/ - const char_t *font_family(const Font *font) { cassert_no_null(font); @@ -168,24 +248,28 @@ real32_t font_size(const Font *font) /*---------------------------------------------------------------------------*/ -static ___INLINE void i_metrics(Font *font) +real32_t font_height(const Font *font) { cassert_no_null(font); - if (font->metrics == FALSE) - { - i_osfont(font); - osfont_metrics(font->osfont, font->size, &font->ascent, &font->descent, &font->leading, &font->cell_size, &font->monospace); - font->metrics = TRUE; - } + i_metrics(cast(font, Font)); + return font->cell_size; } /*---------------------------------------------------------------------------*/ -real32_t font_height(const Font *font) +real32_t font_width(const Font *font) { cassert_no_null(font); i_metrics(cast(font, Font)); - return font->cell_size; + return font->avg_width; +} + +/*---------------------------------------------------------------------------*/ + +real32_t font_xscale(const Font *font) +{ + cassert_no_null(font); + return font->xscale; } /*---------------------------------------------------------------------------*/ @@ -238,7 +322,7 @@ void font_extents(const Font *font, const char_t *text, const real32_t refwidth, { cassert_no_null(font); i_osfont(cast(font, Font)); - osfont_extents(font->osfont, text, refwidth, width, height); + osfont_extents(font->osfont, text, font->xscale, refwidth, width, height); } /*---------------------------------------------------------------------------*/ @@ -248,4 +332,4 @@ const void *font_native(const Font *font) cassert_no_null(font); i_osfont(cast(font, Font)); return font->osfont; -} +} \ No newline at end of file diff --git a/src/draw2d/font.h b/src/draw2d/font.h index 816c49be..39d380c1 100644 --- a/src/draw2d/font.h +++ b/src/draw2d/font.h @@ -23,6 +23,10 @@ _draw2d_api Font *font_monospace(const real32_t size, const uint32_t style); _draw2d_api Font *font_with_style(const Font *font, const uint32_t style); +_draw2d_api Font *font_with_width(const Font *font, const real32_t width); + +_draw2d_api Font *font_with_xscale(const Font *font, const real32_t scale); + _draw2d_api Font *font_copy(const Font *font); _draw2d_api void font_destroy(Font **font); @@ -41,6 +45,10 @@ _draw2d_api real32_t font_size(const Font *font); _draw2d_api real32_t font_height(const Font *font); +_draw2d_api real32_t font_width(const Font *font); + +_draw2d_api real32_t font_xscale(const Font *font); + _draw2d_api real32_t font_ascent(const Font *font); _draw2d_api real32_t font_descent(const Font *font); @@ -61,4 +69,4 @@ _draw2d_api ArrPt(String) *font_installed_monospace(void); _draw2d_api const void *font_native(const Font *font); -__END_C +__END_C \ No newline at end of file diff --git a/src/draw2d/font.inl b/src/draw2d/font.inl index e911536f..697fdd3a 100644 --- a/src/draw2d/font.inl +++ b/src/draw2d/font.inl @@ -18,7 +18,7 @@ void osfont_alloc_globals(void); void osfont_dealloc_globals(void); -OSFont *osfont_create(const char_t *family, const real32_t size, const uint32_t style); +OSFont *osfont_create(const char_t *family, const real32_t size, const real32_t width, const real32_t xscale, const uint32_t style); void osfont_destroy(OSFont **font); @@ -26,10 +26,10 @@ String *osfont_family_name(const OSFont *font); font_family_t osfont_system(const char_t *family); -void osfont_metrics(const OSFont *font, const real32_t size, real32_t *ascent, real32_t *descent, real32_t *leading, real32_t *cell_size, bool_t *monospace); +void osfont_metrics(const OSFont *font, const real32_t size, const real32_t xscale, real32_t *ascent, real32_t *descent, real32_t *leading, real32_t *cell_size, real32_t *avg_width, bool_t *monospace); -void osfont_extents(const OSFont *font, const char_t *text, const real32_t refwidth, real32_t *width, real32_t *height); +void osfont_extents(const OSFont *font, const char_t *text, const real32_t xscale, const real32_t refwidth, real32_t *width, real32_t *height); const void *osfont_native(const OSFont *font); -__END_C +__END_C \ No newline at end of file diff --git a/src/draw2d/gtk/dctx_gtk.c b/src/draw2d/gtk/dctx_gtk.c index f415f70c..1a1be934 100644 --- a/src/draw2d/gtk/dctx_gtk.c +++ b/src/draw2d/gtk/dctx_gtk.c @@ -42,6 +42,13 @@ void dctx_destroy(DCtx **ctx) cassert_no_null(ctx); cassert_no_null(*ctx); + if ((*ctx)->layout != NULL) + { + g_object_unref((*ctx)->layout); + (*ctx)->layout = NULL; + } + + /* Image-based context */ if ((*ctx)->surface != NULL) { cairo_surface_destroy((*ctx)->surface); @@ -58,9 +65,6 @@ void dctx_destroy(DCtx **ctx) if ((*ctx)->font != NULL) font_destroy(&(*ctx)->font); - if ((*ctx)->layout != NULL) - g_object_unref((*ctx)->layout); - heap_delete(ctx, DCtx); } diff --git a/src/draw2d/gtk/draw_gtk.c b/src/draw2d/gtk/draw_gtk.c index b64fac31..307d5c59 100644 --- a/src/draw2d/gtk/draw_gtk.c +++ b/src/draw2d/gtk/draw_gtk.c @@ -579,29 +579,41 @@ static void i_begin_text(DCtx *ctx, const char_t *text, const real32_t x, const { double nx = (double)x; double ny = (double)y; - + real32_t xscale = 1.f; cassert_no_null(ctx); if (ctx->layout == NULL) { const PangoFontDescription *fdesc = NULL; + cairo_matrix_t matrix; cassert(ctx->font != NULL); fdesc = (PangoFontDescription *)font_native(ctx->font); + /* + * Caution! If cairo context has rotations/scales in its transform matrix + * they will inherited in PangoContext matrix, warping the text. + * All text transforms are managed by cairo matrix. + * PangoLayout MUST avoid rotations/scales in its PangoContext matrix. + * Important: Never use 'pango_cairo_update_layout()' + * Additionally, PangoLayout does not correctly handle text scaling. + */ + cairo_get_matrix(ctx->cairo, &matrix); + cairo_set_matrix(ctx->cairo, &ctx->origin); ctx->layout = pango_cairo_create_layout(ctx->cairo); + cairo_set_matrix(ctx->cairo, &matrix); pango_layout_set_font_description(ctx->layout, fdesc); } + xscale = font_xscale(ctx->font); pango_layout_set_text(ctx->layout, (const char *)text, -1); pango_layout_set_alignment(ctx->layout, ctx->text_intalign); - pango_layout_set_width(ctx->layout, ctx->text_width < 0 ? -1 : (int)(ctx->text_width * PANGO_SCALE)); - /* pango_layout_set_wrap(ctx->layout, ctx->text_width < 0 ? PANGO_WRAP_CHAR); */ + pango_layout_set_width(ctx->layout, ctx->text_width < 0 ? -1 : (int)((ctx->text_width / xscale) * PANGO_SCALE)); pango_layout_set_ellipsize(ctx->layout, ctx->ellipsis); if (ctx->text_halign != ekLEFT || ctx->text_valign != ekTOP) { int w, h; pango_layout_get_pixel_size(ctx->layout, &w, &h); - + w = (int)((real32_t)w * xscale); switch (ctx->text_halign) { case ekLEFT: @@ -631,6 +643,8 @@ static void i_begin_text(DCtx *ctx, const char_t *text, const real32_t x, const } } + cairo_save(ctx->cairo); + if (ctx->cartesian_system == TRUE) { cairo_matrix_t matrix; @@ -640,11 +654,13 @@ static void i_begin_text(DCtx *ctx, const char_t *text, const real32_t x, const matrix.yy = 1; matrix.x0 = 0; matrix.y0 = 0; - cairo_save(ctx->cairo); cairo_transform(ctx->cairo, &matrix); } cairo_move_to(ctx->cairo, nx, ny); + cairo_translate(ctx->cairo, nx, ny); + cairo_scale(ctx->cairo, xscale, 1); + cairo_translate(ctx->cairo, -nx, -ny); } /*---------------------------------------------------------------------------*/ @@ -657,8 +673,7 @@ void draw_text(DCtx *ctx, const char_t *text, const real32_t x, const real32_t y i_begin_text(ctx, text, x, y); i_color(ctx->cairo, ctx->text_color, &ctx->source_color); pango_cairo_show_layout(ctx->cairo, ctx->layout); - if (ctx->cartesian_system == TRUE) - cairo_restore(ctx->cairo); + cairo_restore(ctx->cairo); } /*---------------------------------------------------------------------------*/ @@ -693,8 +708,7 @@ void draw_text_path(DCtx *ctx, const drawop_t op, const char_t *text, const real i_draw(ctx, op); } - if (ctx->cartesian_system == TRUE) - cairo_restore(ctx->cairo); + cairo_restore(ctx->cairo); } /*---------------------------------------------------------------------------*/ @@ -774,22 +788,13 @@ void draw_text_halign(DCtx *ctx, const align_t halign) void draw_text_extents(DCtx *ctx, const char_t *text, const real32_t refwidth, real32_t *width, real32_t *height) { - int w, h; + /* + * In Linux/GTK drawing context uses the same method (PangoLayout associated + * with Cairo context) to measure and render the text than 'font_extents()'. + * Just bypass the call. + */ cassert_no_null(ctx); - if (ctx->layout == NULL) - { - const PangoFontDescription *fdesc = NULL; - cassert(ctx->font != NULL); - fdesc = (PangoFontDescription *)font_native(ctx->font); - ctx->layout = pango_cairo_create_layout(ctx->cairo); - pango_layout_set_font_description(ctx->layout, fdesc); - } - - pango_layout_set_text(ctx->layout, (const char *)text, -1); - pango_layout_set_width(ctx->layout, refwidth < 0 ? -1 : (int)(refwidth * PANGO_SCALE)); - pango_layout_get_pixel_size(ctx->layout, &w, &h); - ptr_assign(width, (real32_t)w); - ptr_assign(height, (real32_t)h); + font_extents(ctx->font, text, refwidth, width, height); } /*---------------------------------------------------------------------------*/ @@ -818,6 +823,7 @@ void draw_text_raster(DCtx *ctx, const char_t *text, const real32_t x, const rea i_begin_text(ctx, text, x, y); i_color(ctx->cairo, ctx->text_color, &ctx->source_color); pango_cairo_show_layout(ctx->cairo, ctx->layout); + cairo_restore(ctx->cairo); } /*---------------------------------------------------------------------------*/ diff --git a/src/draw2d/gtk/osfont.c b/src/draw2d/gtk/osfont.c index 85e7bb84..b6fc5563 100644 --- a/src/draw2d/gtk/osfont.c +++ b/src/draw2d/gtk/osfont.c @@ -60,6 +60,14 @@ void osfont_dealloc_globals(void) /*---------------------------------------------------------------------------*/ +static const char_t *i_monospace_font_family(void) +{ + const char_t *desired_fonts[] = {"Ubuntu Mono", "DejaVu Sans Mono", "Courier New"}; + return draw2d_monospace_family(desired_fonts, sizeof(desired_fonts) / sizeof(const char_t *)); +} + +/*---------------------------------------------------------------------------*/ + static real32_t i_device_to_pixels(void) { if (i_PANGO_TO_PIXELS < 0) @@ -75,30 +83,6 @@ static real32_t i_device_to_pixels(void) /*---------------------------------------------------------------------------*/ -real32_t font_regular_size(void) -{ - cassert(i_SYSTEM_FONT_FAMILY != NULL); - return kFONT_REGULAR_SIZE; -} - -/*---------------------------------------------------------------------------*/ - -real32_t font_small_size(void) -{ - cassert(i_SYSTEM_FONT_FAMILY != NULL); - return kFONT_SMALL_SIZE; -} - -/*---------------------------------------------------------------------------*/ - -real32_t font_mini_size(void) -{ - cassert(i_SYSTEM_FONT_FAMILY != NULL); - return kFONT_MINI_SIZE; -} - -/*---------------------------------------------------------------------------*/ - static gint i_font_size(const real32_t size, const uint32_t style) { if ((style & ekFPOINTS) == ekFPOINTS) @@ -114,20 +98,30 @@ static gint i_font_size(const real32_t size, const uint32_t style) /*---------------------------------------------------------------------------*/ -static const char_t *i_monospace_font_family(void) +static gint i_scale_size_to_cell(const real32_t size, const int pango_size, PangoFontDescription *font) { - const char_t *desired_fonts[] = {"Ubuntu Mono", "DejaVu Sans Mono", "Courier New"}; - return draw2d_monospace_family(desired_fonts, sizeof(desired_fonts) / sizeof(const char_t *)); + real32_t cell_size = 1e8f; + gint new_pango_size = 0; + osfont_extents(cast(font, OSFont), "REFTEXT", 1, -1, NULL, &cell_size); + new_pango_size = (gint)((size * (real32_t)pango_size) / cell_size); + return new_pango_size; } /*---------------------------------------------------------------------------*/ -OSFont *osfont_create(const char_t *family, const real32_t size, const uint32_t style) +OSFont *osfont_create(const char_t *family, const real32_t size, const real32_t width, const real32_t xscale, const uint32_t style) { const char_t *name = NULL; gint psize = 0; PangoFontDescription *font = NULL; + /* + * In Pango, font width or scale is not assigned as PangoFontDescription property + * With be use as scale factor in Cairo contexts by font_xscale() + */ + unref(width); + unref(xscale); + if (str_equ_c(family, "__SYSTEM__") == TRUE) { cassert(i_SYSTEM_FONT_FAMILY != NULL); @@ -148,6 +142,13 @@ OSFont *osfont_create(const char_t *family, const real32_t size, const uint32_t pango_font_description_set_size(font, psize); pango_font_description_set_style(font, (style & ekFITALIC) == ekFITALIC ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL); pango_font_description_set_weight(font, (style & ekFBOLD) == ekFBOLD ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL); + + if ((style & ekFCELL) == ekFCELL) + { + gint s = i_scale_size_to_cell(size, psize, font); + pango_font_description_set_size(font, s); + } + heap_auditor_add("PangoFontDescription"); return cast(font, OSFont); } @@ -165,34 +166,6 @@ void osfont_destroy(OSFont **font) /*---------------------------------------------------------------------------*/ -void osfont_extents(const OSFont *font, const char_t *text, const real32_t refwidth, real32_t *width, real32_t *height) -{ - int w, h; - cassert_no_null(font); - if (i_CAIRO == NULL) - { - i_CAIRO = cairo_create(NULL); - i_LAYOUT = pango_cairo_create_layout(i_CAIRO); - } - - pango_layout_set_font_description(i_LAYOUT, cast(font, PangoFontDescription)); - pango_layout_set_text(i_LAYOUT, (const char *)text, -1); - pango_layout_set_width(i_LAYOUT, refwidth < 0 ? -1 : (int)(refwidth * PANGO_SCALE)); - pango_layout_get_pixel_size(i_LAYOUT, &w, &h); - ptr_assign(width, (real32_t)w); - ptr_assign(height, (real32_t)h); -} - -/*---------------------------------------------------------------------------*/ - -const void *osfont_native(const OSFont *font) -{ - cassert_no_null(font); - return cast(font, void); -} - -/*---------------------------------------------------------------------------*/ - String *osfont_family_name(const OSFont *font) { const PangoFontDescription *ffont = cast_const(font, PangoFontDescription); @@ -246,7 +219,7 @@ static bool_t i_is_monospace(PangoFontMap *fontmap, const PangoFontDescription * /*---------------------------------------------------------------------------*/ -void osfont_metrics(const OSFont *font, const real32_t size, real32_t *ascent, real32_t *descent, real32_t *leading, real32_t *cell_size, bool_t *monospace) +void osfont_metrics(const OSFont *font, const real32_t size, const real32_t xscale, real32_t *ascent, real32_t *descent, real32_t *leading, real32_t *cell_size, real32_t *avg_width, bool_t *monospace) { /* This object is owned by Pango and must not be freed */ PangoFontMap *fontmap = pango_cairo_font_map_get_default(); @@ -264,19 +237,22 @@ void osfont_metrics(const OSFont *font, const real32_t size, real32_t *ascent, r if (descent != NULL) *descent = (real32_t)(pango_font_metrics_get_descent(metrics) / PANGO_SCALE); - if (leading != NULL) + /* We need to get a real text measure */ + if (leading != NULL || cell_size != NULL || avg_width != NULL) { real32_t width, height; - osfont_extents(font, "O", -1, &width, &height); - unref(width); - *leading = height - size; - } + uint32_t len; + const char_t *str = draw2d_str_avg_char_width(&len); + osfont_extents(font, str, xscale, -1, &width, &height); - if (cell_size != NULL) - { - real32_t width; - osfont_extents(font, "O", -1, &width, cell_size); - unref(width); + if (leading != NULL) + *leading = height - size; + + if (cell_size != NULL) + *cell_size = height; + + if (avg_width != NULL) + *avg_width = width / len; } if (monospace != NULL) @@ -289,6 +265,58 @@ void osfont_metrics(const OSFont *font, const real32_t size, real32_t *ascent, r /*---------------------------------------------------------------------------*/ +void osfont_extents(const OSFont *font, const char_t *text, const real32_t xscale, const real32_t refwidth, real32_t *width, real32_t *height) +{ + int w, h; + cassert_no_null(font); + if (i_CAIRO == NULL) + { + i_CAIRO = cairo_create(NULL); + i_LAYOUT = pango_cairo_create_layout(i_CAIRO); + } + + pango_layout_set_font_description(i_LAYOUT, cast(font, PangoFontDescription)); + pango_layout_set_text(i_LAYOUT, (const char *)text, -1); + pango_layout_set_width(i_LAYOUT, refwidth < 0 ? -1 : (int)((refwidth / xscale) * PANGO_SCALE)); + pango_layout_get_pixel_size(i_LAYOUT, &w, &h); + ptr_assign(width, (real32_t)w * xscale); + ptr_assign(height, (real32_t)h); +} + +/*---------------------------------------------------------------------------*/ + +const void *osfont_native(const OSFont *font) +{ + cassert_no_null(font); + return cast(font, void); +} + +/*---------------------------------------------------------------------------*/ + +real32_t font_regular_size(void) +{ + cassert(i_SYSTEM_FONT_FAMILY != NULL); + return kFONT_REGULAR_SIZE; +} + +/*---------------------------------------------------------------------------*/ + +real32_t font_small_size(void) +{ + cassert(i_SYSTEM_FONT_FAMILY != NULL); + return kFONT_SMALL_SIZE; +} + +/*---------------------------------------------------------------------------*/ + +real32_t font_mini_size(void) +{ + cassert(i_SYSTEM_FONT_FAMILY != NULL); + return kFONT_MINI_SIZE; +} + +/*---------------------------------------------------------------------------*/ + const char_t *font_register(const byte_t *data, const uint32_t size) { unref(data); @@ -374,4 +402,4 @@ void dctx_set_default_osfont(DCtx *ctx, const void *font) kFONT_REGULAR_SIZE = size * scale; kFONT_SMALL_SIZE = kFONT_REGULAR_SIZE - 2.f; kFONT_MINI_SIZE = kFONT_REGULAR_SIZE - 4.f; -} +} \ No newline at end of file diff --git a/src/draw2d/osx/dctx_osx.m b/src/draw2d/osx/dctx_osx.m index d5a67db9..473ba7bb 100644 --- a/src/draw2d/osx/dctx_osx.m +++ b/src/draw2d/osx/dctx_osx.m @@ -34,23 +34,22 @@ static void i_init_text_attr(DCtx *ctx) { id objects[5]; id keys[5]; - cassert(ctx->text_parag == NULL); cassert(ctx->text_dict == NULL); + cassert(ctx->font == NULL); + ctx->font = font_system(font_regular_size(), 0); ctx->text_parag = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain]; [ctx->text_parag setLineBreakMode:NSLineBreakByWordWrapping]; - objects[0] = kUNDERLINE_NONE; objects[1] = kUNDERLINE_NONE; objects[2] = ctx->text_parag; objects[3] = [NSColor blackColor]; - objects[4] = [NSFont systemFontOfSize:[NSFont systemFontSize]]; + objects[4] = (NSFont *)font_native(ctx->font); keys[0] = NSUnderlineStyleAttributeName; keys[1] = NSStrikethroughStyleAttributeName; keys[2] = NSParagraphStyleAttributeName; keys[3] = NSForegroundColorAttributeName; keys[4] = NSFontAttributeName; - ctx->text_dict = [[NSMutableDictionary alloc] initWithObjects:objects forKeys:keys count:5]; } @@ -83,6 +82,7 @@ void dctx_destroy(DCtx **ctx) [(*ctx)->text_dict release]; [(*ctx)->text_parag release]; + font_destroy(&(*ctx)->font); if ((*ctx)->data != NULL) { diff --git a/src/draw2d/osx/draw2d_osx.ixx b/src/draw2d/osx/draw2d_osx.ixx index 272e9827..ffac4989 100644 --- a/src/draw2d/osx/draw2d_osx.ixx +++ b/src/draw2d/osx/draw2d_osx.ixx @@ -38,6 +38,7 @@ struct _dctx_t CGAffineTransform transform; CGGradientRef gradient; CGAffineTransform gradient_matrix; + Font *font; color_t skcolor; color_t fillcolor; color_t text_color; diff --git a/src/draw2d/osx/draw_osx.m b/src/draw2d/osx/draw_osx.m index c3204409..c4289b36 100644 --- a/src/draw2d/osx/draw_osx.m +++ b/src/draw2d/osx/draw_osx.m @@ -812,12 +812,16 @@ void draw_fill_wrap(DCtx *ctx, const fillwrap_t wrap) void draw_font(DCtx *ctx, const Font *font) { - uint32_t fstyle; cassert_no_null(ctx); - fstyle = font_style(font); - [ctx->text_dict setObject:(fstyle & ekFUNDERLINE) ? kUNDERLINE_SINGLE : kUNDERLINE_NONE forKey:NSUnderlineStyleAttributeName]; - [ctx->text_dict setObject:(fstyle & ekFSTRIKEOUT) ? kUNDERLINE_SINGLE : kUNDERLINE_NONE forKey:NSStrikethroughStyleAttributeName]; - [ctx->text_dict setObject:(NSFont *)font_native(font) forKey:NSFontAttributeName]; + if (font_equals(ctx->font, font) == FALSE) + { + uint32_t fstyle = font_style(font); + font_destroy(&ctx->font); + ctx->font = font_copy(font); + [ctx->text_dict setObject:(fstyle & ekFUNDERLINE) ? kUNDERLINE_SINGLE : kUNDERLINE_NONE forKey:NSUnderlineStyleAttributeName]; + [ctx->text_dict setObject:(fstyle & ekFSTRIKEOUT) ? kUNDERLINE_SINGLE : kUNDERLINE_NONE forKey:NSStrikethroughStyleAttributeName]; + [ctx->text_dict setObject:(NSFont *)font_native(ctx->font) forKey:NSFontAttributeName]; + } } /*---------------------------------------------------------------------------*/ @@ -1127,38 +1131,15 @@ void draw_text_halign(DCtx *ctx, const align_t halign) /*---------------------------------------------------------------------------*/ -static bool_t i_with_newline(const char_t *text) -{ - cassert_no_null(text); - while (*text != 0) - { - if (*text == '\n') - return TRUE; - text += 1; - } - - return FALSE; -} - -/*---------------------------------------------------------------------------*/ - void draw_text_extents(DCtx *ctx, const char_t *text, const real32_t refwidth, real32_t *width, real32_t *height) { - MeasureStr data; + /* + * In macOS drawing context uses the same method (CGContextRef with NSFont) + * to measure and render the text than 'font_extents()'. + * Just bypass the call. + */ cassert_no_null(ctx); - cassert_no_null(width); - cassert_no_null(height); - data.dict = ctx->text_dict; - if (refwidth > 0 || i_with_newline(text) == TRUE) - { - draw2d_extents(&data, draw_word_extents, TRUE, text, refwidth, width, height, MeasureStr); - } - else - { - draw_word_extents(&data, text, width, height); - *width = bmath_ceilf(*width); - *height = bmath_ceilf(*height); - } + font_extents(ctx->font, text, refwidth, width, height); } /*---------------------------------------------------------------------------*/ diff --git a/src/draw2d/osx/osfont.m b/src/draw2d/osx/osfont.m index cf4b55b5..7a76fa4c 100644 --- a/src/draw2d/osx/osfont.m +++ b/src/draw2d/osx/osfont.m @@ -21,6 +21,7 @@ #include "draw.inl" #include #include +#include #include #if !defined(__MACOS__) @@ -29,11 +30,26 @@ static String *i_SYSTEM_FONT_FAMILY = NULL; static String *i_MONOSPACE_FONT_FAMILY = NULL; +static bool_t i_SYSTEM_FONT_REDUCED = FALSE; /*---------------------------------------------------------------------------*/ void osfont_alloc_globals(void) { + /* + * I don't know why, in some newer macOS the system default font + * has a great font scaling by default. This affect when apply font + * transform, necessary for some fonts italic shear and x-scaling. + * This code detects if the system font has this big scaling + */ + real32_t xscale = 1.1f; + OSFont *font = osfont_create("__SYSTEM__", font_regular_size(), -1, xscale, 0); + real32_t w, h; + osfont_extents(font, "OO", -1, xscale, &w, &h); + osfont_destroy(&font); + unref(w); + if (h > 40) + i_SYSTEM_FONT_REDUCED = TRUE; } /*---------------------------------------------------------------------------*/ @@ -71,36 +87,50 @@ real32_t font_mini_size(void) { if (i_MONOSPACE_FONT_FAMILY == NULL) { - /* From Catalina, we have a system predefined monospace font */ -#if defined(MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15 - cassert(FALSE); - return NULL; -#else const char_t *desired_fonts[] = {"SF Mono", "Menlo", "Monaco", "Andale Mono", "Courier New"}; const char_t *monofont = draw2d_monospace_family(desired_fonts, sizeof(desired_fonts) / sizeof(const char_t *)); i_MONOSPACE_FONT_FAMILY = str_c(monofont); -#endif } return tc(i_MONOSPACE_FONT_FAMILY); } /*---------------------------------------------------------------------------*/ - -static NSFont *i_convent_to_italic(NSFont *font, const CGFloat height, NSFontManager *font_manager) +/* + * This funtion apply two transforms: + * - A shear if itatic is required. + * - A x-scale if we need a char width different that font defaults. + */ +static NSFont *i_font_transform(NSFont *font, const CGFloat xscale, const CGFloat height, const BOOL italic, NSFontManager *font_manager) { - NSFont *italic_font = nil; - NSFontTraitMask fontTraits = (NSFontTraitMask)0; + NSFont *tfont = nil; + BOOL with_italic = NO; cassert_no_null(font); - italic_font = [font_manager convertFont:font toHaveTrait:NSItalicFontMask]; - fontTraits = [font_manager traitsOfFont:italic_font]; + if (italic == YES) + { + /* The NSFontManager can apply the italic without affine */ + NSFontTraitMask traits = 0; + tfont = [font_manager convertFont:font toHaveTrait:NSItalicFontMask]; + traits = [font_manager traitsOfFont:tfont]; + if ((traits & NSItalicFontMask) == NSItalicFontMask) + with_italic = YES; + } + else + { + tfont = font; + } - if ((fontTraits & NSItalicFontMask) == 0) + /* We have to apply an affine transform to text */ + if ((italic == YES && with_italic == NO) || (xscale > 0 && fabs((double)xscale - 1) > 0.01)) { NSAffineTransform *font_transform = [NSAffineTransform transform]; - [font_transform scaleBy:height]; + /* Apply the x-scale */ + [font_transform scaleXBy:xscale * height yBy:height]; + + /* Italic is required, but don't apply by FontManager */ + if (italic == YES && with_italic == NO) { NSAffineTransformStruct data; NSAffineTransform *italic_transform = nil; @@ -115,15 +145,15 @@ real32_t font_mini_size(void) [font_transform appendTransform:italic_transform]; } - italic_font = [NSFont fontWithDescriptor:[italic_font fontDescriptor] textTransform:font_transform]; + tfont = [NSFont fontWithDescriptor:[tfont fontDescriptor] textTransform:font_transform]; } - return italic_font; + return tfont; } /*---------------------------------------------------------------------------*/ -OSFont *osfont_create(const char_t *family, const real32_t size, const uint32_t style) +static NSFont *i_nsfont(const char_t *family, const real32_t size, const uint32_t style) { const char_t *name = NULL; NSFont *nsfont = nil; @@ -140,13 +170,17 @@ real32_t font_mini_size(void) { /* From Catalina, we have a system predefined monospace font */ #if defined(MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15 - if (style & ekFBOLD) - nsfont = [NSFont monospacedSystemFontOfSize:(CGFloat)size weight:NSFontWeightBold]; - else - nsfont = [NSFont monospacedSystemFontOfSize:(CGFloat)size weight:NSFontWeightLight]; -#else - name = i_monospace_font_family(); + if (draw2d_get_preferred_monospace() == NULL) + { + if (style & ekFBOLD) + nsfont = [NSFont monospacedSystemFontOfSize:(CGFloat)size weight:NSFontWeightBold]; + else + nsfont = [NSFont monospacedSystemFontOfSize:(CGFloat)size weight:NSFontWeightLight]; + } #endif + + if (nsfont == nil) + name = i_monospace_font_family(); } else { @@ -161,14 +195,45 @@ real32_t font_mini_size(void) nsfont = [fontManager fontWithFamily:ffamily traits:(NSFontTraitMask)mask weight:5 size:(CGFloat)size]; } + return nsfont; +} + +/*---------------------------------------------------------------------------*/ + +static real32_t i_cell_size(NSFont *font, const real32_t size) +{ + const char_t *reftext = "ABCDEabcde"; + real32_t twidth = 0, theight = 0; + real32_t scale = 0; + osfont_extents(cast(font, OSFont), reftext, 1, -1, &twidth, &theight); + scale = size / theight; + return bmath_floorf(size * scale); +} + +/*---------------------------------------------------------------------------*/ + +OSFont *osfont_create(const char_t *family, const real32_t size, const real32_t width, const real32_t xscale, const uint32_t style) +{ + real32_t esize = size; + NSFont *nsfont = i_nsfont(family, esize, style); cassert_fatal_msg(nsfont != nil, "Font is not available on this computer."); + unref(width); + + if ((style & ekFCELL) == ekFCELL) + { + esize = i_cell_size(nsfont, esize); + nsfont = i_nsfont(family, esize, style); + } if (nsfont != nil) { - if (style & ekFITALIC) + BOOL with_italic = (style & ekFITALIC) == ekFITALIC; + if (with_italic || (xscale > 0 && fabsf(xscale - 1) > 0.01)) { - NSFontManager *fontManager = [NSFontManager sharedFontManager]; - nsfont = i_convent_to_italic(nsfont, (CGFloat)size, fontManager); + NSFontManager *manager = [NSFontManager sharedFontManager]; + if (str_equ_c(family, "__SYSTEM__") == TRUE && i_SYSTEM_FONT_REDUCED == TRUE) + esize /= size; + nsfont = i_font_transform(nsfont, (CGFloat)xscale, (CGFloat)esize, with_italic, manager); } [nsfont retain]; @@ -231,7 +296,7 @@ font_family_t osfont_system(const char_t *family) /*---------------------------------------------------------------------------*/ -void osfont_metrics(const OSFont *font, const real32_t size, real32_t *ascent, real32_t *descent, real32_t *leading, real32_t *cell_size, bool_t *monospace) +void osfont_metrics(const OSFont *font, const real32_t size, const real32_t xscale, real32_t *ascent, real32_t *descent, real32_t *leading, real32_t *cell_size, real32_t *avg_width, bool_t *monospace) { NSFont *nsfont = cast(font, NSFont); @@ -241,19 +306,22 @@ void osfont_metrics(const OSFont *font, const real32_t size, real32_t *ascent, r if (descent != NULL) *descent = (real32_t) - [nsfont descender]; - if (leading != NULL) + /* We need to get a real text measure */ + if (leading != NULL || cell_size != NULL || avg_width != NULL) { real32_t width, height; - osfont_extents(font, "O", -1, &width, &height); - unref(width); - *leading = height - size; - } + uint32_t len; + const char_t *str = draw2d_str_avg_char_width(&len); + osfont_extents(font, str, xscale, -1, &width, &height); - if (cell_size != NULL) - { - real32_t width; - osfont_extents(font, "O", -1, &width, cell_size); - unref(width); + if (leading != NULL) + *leading = height - size; + + if (cell_size != NULL) + *cell_size = height; + + if (avg_width != NULL) + *avg_width = width / len; } if (monospace != NULL) @@ -262,7 +330,7 @@ void osfont_metrics(const OSFont *font, const real32_t size, real32_t *ascent, r /*---------------------------------------------------------------------------*/ -void osfont_extents(const OSFont *font, const char_t *text, const real32_t refwidth, real32_t *width, real32_t *height) +void osfont_extents(const OSFont *font, const char_t *text, const real32_t xscale, const real32_t refwidth, real32_t *width, real32_t *height) { id objects[1]; id keys[1]; @@ -270,6 +338,7 @@ void osfont_extents(const OSFont *font, const char_t *text, const real32_t refwi NSUInteger count = sizeof(objects) / sizeof(id); cassert_no_null(font); cassert(count == 1); + unref(xscale); objects[0] = cast(font, NSFont); keys[0] = NSFontAttributeName; data.dict = [NSDictionary dictionaryWithObjects:objects forKeys:keys count:count]; diff --git a/src/draw2d/win/draw_win.cpp b/src/draw2d/win/draw_win.cpp index e4e31f1b..6bf4ee56 100644 --- a/src/draw2d/win/draw_win.cpp +++ b/src/draw2d/win/draw_win.cpp @@ -774,11 +774,13 @@ static Gdiplus::RectF i_text_origin(DCtx *ctx, const WCHAR *wtext, const real32_ { Gdiplus::RectF layout; Gdiplus::RectF out; + real32_t xscale = font_xscale(ctx->font); layout.X = 0; layout.Y = 0; - layout.Width = (Gdiplus::REAL)(ctx->text_width > 0 ? ctx->text_width : 1e8); + layout.Width = (Gdiplus::REAL)(ctx->text_width > 0 ? ctx->text_width / xscale : 1e8); layout.Height = 1e8; ctx->graphics->MeasureString(wtext, -1, ctx->ffont, layout, &out); + out.Width *= xscale; if (ctx->text_width > 0 && ctx->text_ellipsis != ekELLIPMLINE) { @@ -863,18 +865,6 @@ static Gdiplus::RectF i_text_origin(DCtx *ctx, const WCHAR *wtext, const real32_ break; cassert_default(); } - // switch (ctx->text_intalign) { - // case ekLEFT: - // case ekJUSTIFY: - // break; - // case ekRIGHT: - // origin.X += out.Width; - // break; - // case ekCENTER: - // origin.X += out.Width / 2; - // break; - // cassert_default(); - // } } return Gdiplus::RectF(origin, size); @@ -890,10 +880,13 @@ void draw_text(DCtx *ctx, const char_t *text, const real32_t x, const real32_t y WCHAR *wtext_alloc = NULL; Gdiplus::StringFormat format; Gdiplus::RectF rect; + Gdiplus::Matrix matrix; + real32_t xscale = 1.f; cassert_no_null(ctx); cassert_no_null(ctx->graphics); i_set_gdiplus_mode(ctx); num_chars = 1 + unicode_nchars(text, ekUTF8); + if (num_chars < 1024) { wtext = wtext_static; @@ -911,6 +904,15 @@ void draw_text(DCtx *ctx, const char_t *text, const real32_t x, const real32_t y rect = i_text_origin(ctx, wtext, x, y); format.SetAlignment(i_align(ctx->text_intalign)); + xscale = font_xscale(ctx->font); + + if (bmath_absf(xscale - 1) > 0.01f) + { + ctx->graphics->GetTransform(&matrix); + ctx->graphics->TranslateTransform(rect.X, rect.Y); + ctx->graphics->ScaleTransform((Gdiplus::REAL)xscale, 1); + ctx->graphics->TranslateTransform(-rect.X, -rect.Y); + } if (ctx->text_width < 0) { @@ -918,6 +920,7 @@ void draw_text(DCtx *ctx, const char_t *text, const real32_t x, const real32_t y } else { + Gdiplus::RectF erect = rect; switch (ctx->text_ellipsis) { case ekELLIPNONE: @@ -936,7 +939,13 @@ void draw_text(DCtx *ctx, const char_t *text, const real32_t x, const real32_t y cassert_default(); } - ctx->graphics->DrawString(wtext, -1, ctx->ffont, rect, &format, ctx->tbrush); + erect.Width /= xscale; + ctx->graphics->DrawString(wtext, -1, ctx->ffont, erect, &format, ctx->tbrush); + } + + if (bmath_absf(xscale - 1) > 0.01f) + { + ctx->graphics->SetTransform(&matrix); } if (wtext_alloc != NULL) @@ -964,6 +973,8 @@ void draw_text_path(DCtx *ctx, const drawop_t op, const char_t *text, const real WCHAR *wtext_alloc = NULL; Gdiplus::StringFormat format; Gdiplus::RectF rect; + Gdiplus::Matrix matrix; + real32_t xscale = 1.f; cassert_no_null(ctx); cassert_no_null(ctx->graphics); i_set_gdiplus_mode(ctx); @@ -987,13 +998,28 @@ void draw_text_path(DCtx *ctx, const drawop_t op, const char_t *text, const real format.SetAlignment(i_align(ctx->text_intalign)); format.SetLineAlignment(i_align(ctx->text_valign)); + xscale = font_xscale(ctx->font); + if (bmath_absf(xscale - 1) > 0.01f) + { + ctx->graphics->GetTransform(&matrix); + ctx->graphics->TranslateTransform(rect.X, rect.Y); + ctx->graphics->ScaleTransform((Gdiplus::REAL)xscale, 1); + ctx->graphics->TranslateTransform(-rect.X, -rect.Y); + } + // Text just solid filled --> Use DrawString if (op == ekFILL && ctx->current_brush == ctx->sbrush) { if (ctx->text_width < 0) + { ctx->graphics->DrawString(wtext, -1, ctx->ffont, Gdiplus::PointF(rect.X, rect.Y), &format, ctx->sbrush); + } else - ctx->graphics->DrawString(wtext, -1, ctx->ffont, rect, &format, ctx->sbrush); + { + Gdiplus::RectF erect = rect; + erect.Width /= xscale; + ctx->graphics->DrawString(wtext, -1, ctx->ffont, erect, &format, ctx->sbrush); + } } // Fancy Text --> Use Path else @@ -1004,6 +1030,11 @@ void draw_text_path(DCtx *ctx, const drawop_t op, const char_t *text, const real i_draw_path(ctx, &path, op); } + if (bmath_absf(xscale - 1) > 0.01f) + { + ctx->graphics->SetTransform(&matrix); + } + if (wtext_alloc != NULL) heap_free((byte_t **)&wtext_alloc, num_chars * sizeof(WCHAR), "OSDrawText"); } @@ -1094,10 +1125,16 @@ void draw_text_halign(DCtx *ctx, const align_t halign) void draw_text_extents(DCtx *ctx, const char_t *text, const real32_t refwidth, real32_t *width, real32_t *height) { + /* + * GDI+ render text slightly different than GDI, event using the same font. + * For this reason, we can't use font_extents() in Windows (based on GDI 'GetTextExtentPoint32'). + * We must use 'MeasureString' that is the correct way to do it in GDI+. + */ uint32_t num_chars = 0; WCHAR *wtext = NULL; WCHAR wtext_static[1024]; WCHAR *wtext_alloc = NULL; + real32_t xscale = 1.f; Gdiplus::RectF layout; Gdiplus::RectF out; cassert_no_null(ctx); @@ -1119,12 +1156,13 @@ void draw_text_extents(DCtx *ctx, const char_t *text, const real32_t refwidth, r cassert_unref(bytes == num_chars * sizeof(WCHAR), bytes); } + xscale = font_xscale(ctx->font); layout.X = 0; layout.Y = 0; - layout.Width = (Gdiplus::REAL)(refwidth > 0 ? refwidth : 1e8); + layout.Width = (Gdiplus::REAL)(refwidth > 0 ? refwidth / xscale : 1e8); layout.Height = 1e8; ctx->graphics->MeasureString(wtext, -1, ctx->ffont, layout, &out); - *width = bmath_ceilf((real32_t)out.Width); + *width = bmath_ceilf((real32_t)out.Width * xscale); *height = bmath_ceilf((real32_t)out.Height); if (wtext_alloc != NULL) diff --git a/src/draw2d/win/osfont.cpp b/src/draw2d/win/osfont.cpp index 27475d64..15f246e8 100644 --- a/src/draw2d/win/osfont.cpp +++ b/src/draw2d/win/osfont.cpp @@ -92,52 +92,23 @@ static void i_metrics(NONCLIENTMETRICS *metrics) /*---------------------------------------------------------------------------*/ -real32_t font_regular_size(void) -{ - NONCLIENTMETRICS metrics; - i_metrics(&metrics); - cassert(metrics.lfMessageFont.lfHeight < 0); - return -(real32_t)metrics.lfMessageFont.lfHeight; -} - -/*---------------------------------------------------------------------------*/ - -real32_t font_small_size(void) -{ - NONCLIENTMETRICS metrics; - i_metrics(&metrics); - cassert(metrics.lfMessageFont.lfHeight < 0); - return -(real32_t)(metrics.lfMessageFont.lfHeight + 2); -} - -/*---------------------------------------------------------------------------*/ - -real32_t font_mini_size(void) -{ - NONCLIENTMETRICS metrics; - i_metrics(&metrics); - cassert(metrics.lfMessageFont.lfHeight < 0); - return -(real32_t)(metrics.lfMessageFont.lfHeight + 4); -} - -/*---------------------------------------------------------------------------*/ - static int i_font_height(const real32_t size, const uint32_t style) { + int height = 0; if ((style & ekFPOINTS) == ekFPOINTS) { - return -(int)((size * (real32_t)kLOG_PIXY) / 72.f); + height = -(int)((size * (real32_t)kLOG_PIXY) / 72.f); } - /* else if ((style & ekFCELL) == ekFCELL) - { - return (int)size; - } - */ else { cassert((style & ekFPIXELS) == ekFPIXELS); - return -(int)size; + height = -(int)size; } + + if ((style & ekFCELL) == ekFCELL) + height = -height; + + return height; } /*---------------------------------------------------------------------------*/ @@ -166,7 +137,7 @@ static const char_t *i_monospace_font_family(void) /*---------------------------------------------------------------------------*/ -OSFont *osfont_create(const char_t *family, const real32_t size, const uint32_t style) +OSFont *osfont_create(const char_t *family, const real32_t size, const real32_t width, const real32_t xscale, const uint32_t style) { const char_t *face_name = NULL; WCHAR face_namew[LF_FULLFACESIZE]; @@ -175,6 +146,7 @@ OSFont *osfont_create(const char_t *family, const real32_t size, const uint32_t cassert_no_null(family); cassert(size > 0.f); + unref(xscale); if (str_equ_c(family, "__SYSTEM__") == TRUE) { @@ -199,7 +171,7 @@ OSFont *osfont_create(const char_t *family, const real32_t size, const uint32_t hfont = CreateFont( nHeight, - PARAM(nWidth, 0), + PARAM(nWidth, width >= 0 ? (int)width : 0), PARAM(nEscapement, 0), PARAM(nOrientation, 0), (style & ekFBOLD) == ekFBOLD ? FW_BOLD : FW_MEDIUM, @@ -270,7 +242,7 @@ font_family_t osfont_system(const char_t *family) /*---------------------------------------------------------------------------*/ -void osfont_metrics(const OSFont *font, const real32_t size, real32_t *ascent, real32_t *descent, real32_t *leading, real32_t *cell_size, bool_t *monospace) +void osfont_metrics(const OSFont *font, const real32_t size, const real32_t xscale, real32_t *ascent, real32_t *descent, real32_t *leading, real32_t *cell_size, real32_t *avg_width, bool_t *monospace) { HWND hwnd = GetDesktopWindow(); HDC hdc = GetDC(hwnd); @@ -291,6 +263,15 @@ void osfont_metrics(const OSFont *font, const real32_t size, real32_t *ascent, r if (cell_size != NULL) *cell_size = (real32_t)lptm.tmHeight; + if (avg_width != NULL) + { + uint32_t len = 0; + real32_t w = 0, h = 0; + const char_t *str = draw2d_str_avg_char_width(&len); + osfont_extents(font, str, xscale, -1, &w, &h); + *avg_width = w / len; + } + if (monospace != NULL) { /* @@ -310,11 +291,12 @@ void osfont_metrics(const OSFont *font, const real32_t size, real32_t *ascent, r /*---------------------------------------------------------------------------*/ -void osfont_extents(const OSFont *font, const char_t *text, const real32_t refwidth, real32_t *width, real32_t *height) +void osfont_extents(const OSFont *font, const char_t *text, const real32_t xscale, const real32_t refwidth, real32_t *width, real32_t *height) { MeasureStr data; HGDIOBJ cfont = NULL; int ret = 0; + unref(xscale); data.hdc = GetDC(NULL); cfont = SelectObject(data.hdc, (HFONT)font); draw2d_extents(&data, draw_word_extents, TRUE, text, refwidth, width, height, MeasureStr); @@ -333,6 +315,36 @@ const void *osfont_native(const OSFont *font) /*---------------------------------------------------------------------------*/ +real32_t font_regular_size(void) +{ + NONCLIENTMETRICS metrics; + i_metrics(&metrics); + cassert(metrics.lfMessageFont.lfHeight < 0); + return -(real32_t)metrics.lfMessageFont.lfHeight; +} + +/*---------------------------------------------------------------------------*/ + +real32_t font_small_size(void) +{ + NONCLIENTMETRICS metrics; + i_metrics(&metrics); + cassert(metrics.lfMessageFont.lfHeight < 0); + return -(real32_t)(metrics.lfMessageFont.lfHeight + 2); +} + +/*---------------------------------------------------------------------------*/ + +real32_t font_mini_size(void) +{ + NONCLIENTMETRICS metrics; + i_metrics(&metrics); + cassert(metrics.lfMessageFont.lfHeight < 0); + return -(real32_t)(metrics.lfMessageFont.lfHeight + 4); +} + +/*---------------------------------------------------------------------------*/ + // const char_t *font_register(const byte_t *data, const uint32_t size); // const char_t *font_register(const byte_t *data, const uint32_t size) //{ @@ -486,4 +498,4 @@ ArrPt(String) *font_installed_families(void) ArrPt(String) *font_installed_monospace(void) { return i_installed_families(TRUE); -} +} \ No newline at end of file diff --git a/src/gui/gui.c b/src/gui/gui.c index 14a6e9c8..fa63970d 100644 --- a/src/gui/gui.c +++ b/src/gui/gui.c @@ -275,13 +275,21 @@ bool_t gui_dark_mode(void) color_t gui_alt_color(const color_t light_color, const color_t dark_color) { - uint32_t n = arrst_size(i_ALTCOLORS, ColorAlt); - ColorAlt *alt = arrst_new(i_ALTCOLORS, ColorAlt); - alt->light = light_color; - alt->dark = dark_color; - cassert(kFIRST_COLOR_ALT + n <= 0xFFFF); - color_indexed((uint16_t)(kFIRST_COLOR_ALT + n), i_DARK_MODE ? alt->dark : alt->light); - return (color_t)(kFIRST_COLOR_ALT + n); + /* Avoid to register twice the same color */ + arrst_foreach_const(alt, i_ALTCOLORS, ColorAlt) + if (alt->light == light_color && alt->dark == dark_color) + return (color_t)(kFIRST_COLOR_ALT + alt_i); + arrst_end() + + { + uint32_t n = arrst_size(i_ALTCOLORS, ColorAlt); + ColorAlt *alt = arrst_new(i_ALTCOLORS, ColorAlt); + alt->light = light_color; + alt->dark = dark_color; + cassert(kFIRST_COLOR_ALT + n <= 0xFFFF); + color_indexed((uint16_t)(kFIRST_COLOR_ALT + n), i_DARK_MODE ? alt->dark : alt->light); + return (color_t)(kFIRST_COLOR_ALT + n); + } } /*---------------------------------------------------------------------------*/ diff --git a/src/gui/listbox.c b/src/gui/listbox.c index b7b8be06..a4c7a324 100644 --- a/src/gui/listbox.c +++ b/src/gui/listbox.c @@ -791,6 +791,21 @@ void listbox_set_elem(ListBox *listbox, const uint32_t index, const char_t *text /*---------------------------------------------------------------------------*/ +void listbox_font(ListBox *listbox, const Font *font) +{ + LData *data = view_get_data(ViewPtr(listbox), LData); + cassert_no_null(data); + if (font_equals(data->font, font) == FALSE) + { + font_destroy(&data->font); + data->font = font_copy(font); + i_document_size(data); + view_update(ViewPtr(listbox)); + } +} + +/*---------------------------------------------------------------------------*/ + void listbox_clear(ListBox *listbox) { LData *data = view_get_data(ViewPtr(listbox), LData); diff --git a/src/gui/listbox.h b/src/gui/listbox.h index 022f5347..908827ae 100644 --- a/src/gui/listbox.h +++ b/src/gui/listbox.h @@ -31,6 +31,8 @@ _gui_api void listbox_add_elem(ListBox *listbox, const char_t *text, const Image _gui_api void listbox_set_elem(ListBox *listbox, const uint32_t index, const char_t *text, const Image *image); +_gui_api void listbox_font(ListBox *listbox, const Font *font); + _gui_api void listbox_clear(ListBox *listbox); _gui_api void listbox_color(ListBox *listbox, const uint32_t index, const color_t color); diff --git a/src/gui/tableview.c b/src/gui/tableview.c index cb896b77..0e7a0a72 100644 --- a/src/gui/tableview.c +++ b/src/gui/tableview.c @@ -690,7 +690,10 @@ static void i_col_y_offset(Column *col, const uint32_t row_height) case ekCTYPE_TEXT: { uint32_t height = i_font_height(col->font); - col->yoffset = (row_height - height) / 2; + if (row_height > height) + col->yoffset = (row_height - height) / 2; + else + col->yoffset = 0; break; } @@ -2114,4 +2117,4 @@ uint32_t tableview_get_focus_row(const TableView *view) void tableview_scroll_visible(TableView *view, const bool_t horizontal, const bool_t vertical) { view_scroll_visible((View *)view, horizontal, vertical); -} +} \ No newline at end of file diff --git a/src/osgui/gtk/osbutton.c b/src/osgui/gtk/osbutton.c index 4ea9f40b..9668f568 100644 --- a/src/osgui/gtk/osbutton.c +++ b/src/osgui/gtk/osbutton.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -38,14 +39,28 @@ struct _osbutton_t button_flag_t flags; bool_t launch_event; bool_t is_default; + uint32_t vpadding; + uint32_t hpadding; + real32_t textwidth; + real32_t textheight; GtkWidget *radio; + Font *fake_font; Font *font; - GtkCssProvider *pfont; + String *text; + String *markup; + Image *image; + GtkCssProvider *css_padding; + GtkCssProvider *css_font; Listener *OnClick; }; /*---------------------------------------------------------------------------*/ +static const uint32_t i_PUSHBUTTON_EXTRAWIDTH = 2; +static const uint32_t i_CHECKBOX_EXTRAHEIGHT = 2; + +/*---------------------------------------------------------------------------*/ + static gui_state_t i_get_state(const OSButton *button) { cassert_no_null(button); @@ -151,17 +166,125 @@ static gboolean i_OnPushDraw(GtkWidget *widget, cairo_t *cr, OSButton *button) /*---------------------------------------------------------------------------*/ -static void i_flat_button_zero_padding(GtkWidget *widget) +static const char_t *i_css_obj(const uint32_t flags) { + /* Initial padding for button */ + switch (button_get_type(flags)) { - const char_t *cssbut = osglobals_css_button(); - char_t css[256]; -#if GTK_CHECK_VERSION(3, 22, 0) - bstd_sprintf(css, sizeof(css), "%s {padding-top:0px;padding-bottom:0px;padding-left:0px;padding-right:0px;min-height:0}", cssbut); -#else - bstd_sprintf(css, sizeof(css), "%s {padding-top:0px;padding-bottom:0px;padding-left:0px;padding-right:0px}", cssbut); -#endif - _oscontrol_widget_set_css(widget, css); + case ekBUTTON_PUSH: + case ekBUTTON_FLAT: + case ekBUTTON_FLATGLE: + return osglobals_css_button(); + case ekBUTTON_RADIO: + return osglobals_css_radio(); + case ekBUTTON_CHECK2: + case ekBUTTON_CHECK3: + return osglobals_css_check(); + cassert_default(); + } + + return ""; +} + +/*---------------------------------------------------------------------------*/ + +static gboolean i_OnLabelDraw(GtkWidget *widget, cairo_t *cr, OSButton *button) +{ + PangoLayout *layout = NULL; + real32_t bwidth = 0, bheight = 0; + cassert_no_null(button); + cassert(GTK_IS_LABEL(widget) == TRUE); + cassert(osbutton_text_allowed(button->flags) == TRUE); + + _oscontrol_widget_get_size(button->control.widget, &bwidth, &bheight); + + /* + * Prepare the PangoLayout to render the button text. + * Overwrite the 'fake' font by font we want. + */ + { + GdkRGBA color; + layout = gtk_label_get_layout(GTK_LABEL(widget)); + pango_layout_set_font_description(layout, cast(font_native(button->font), PangoFontDescription)); + _oscontrol_to_gdkrgba(ekSYSCOLOR_LABEL, &color); + cairo_set_source_rgba(cr, color.red, color.green, color.blue, color.alpha); + pango_layout_set_markup(layout, tc(button->markup), -1); + } + + cairo_save(cr); + + /* Positioning to draw the text */ + if (button_get_type(button->flags) == ekBUTTON_PUSH) + { + /* In pushbuttons, we have to center the content */ + real32_t cwidth = button->textwidth + i_PUSHBUTTON_EXTRAWIDTH; + + if (button->image != NULL) + { + real32_t imgw = image_width(button->image); + cwidth += imgw + kBUTTON_IMAGE_SEP; + cairo_translate(cr, imgw + kBUTTON_IMAGE_SEP, 0); + } + + cairo_translate(cr, (bwidth - cwidth) / 2, ((bheight - button->textheight - 4) / 2)); + } + + /* Font scaling */ + cairo_scale(cr, font_xscale(button->font), 1); + + /* Draw the text */ + pango_cairo_show_layout(cr, layout); + + /* Draw the image. I don't know why, if I render the image first, text is not shown */ + if (button->image != NULL) + { + GdkPixbuf *pixbuf = cast(image_native(button->image), GdkPixbuf); + real32_t imgw = image_width(button->image); + real32_t imgh = image_height(button->image); + cassert(button_get_type(button->flags) == ekBUTTON_PUSH); + gdk_cairo_set_source_pixbuf(cr, pixbuf, -(imgw + kBUTTON_IMAGE_SEP), (button->textheight - imgh) / 2); + cairo_paint(cr); + } + + /* + * All is done. Restore Cairo and the fake font. + * If Gtk detects the 'real' font will change the button dimensions. + */ + cairo_restore(cr); + pango_layout_set_font_description(layout, (PangoFontDescription *)font_native(button->fake_font)); + + /* Stop other handlers from being invoked for the event */ + return TRUE; +} + +/*---------------------------------------------------------------------------*/ + +static GtkWidget *i_get_gtk_label(GtkWidget *widget) +{ + if (GTK_IS_LABEL(widget)) + { + return widget; + } + else if (GTK_IS_CONTAINER(widget)) + { + uint32_t i, n = _oscontrol_num_children(GTK_CONTAINER(widget)); + for (i = 0; i < n; ++i) + { + GtkWidget *child = _oscontrol_get_child(GTK_CONTAINER(widget), i); + GtkWidget *label = i_get_gtk_label(child); + if (label != NULL) + return label; + } + + return NULL; + } + else + { + /* + const gchar *ptype = G_OBJECT_TYPE_NAME(widget); + printf("TYPE: %s\n", ptype); + */ + return NULL; } } @@ -172,12 +295,15 @@ OSButton *osbutton_create(const uint32_t flags) OSButton *button = heap_new0(OSButton); GtkWidget *widget = NULL; GtkWidget *focus_widget = NULL; + const char_t *cssobj = NULL; button->flags = flags; + button->textwidth = 0; + button->textheight = 0; switch (button_get_type(flags)) { case ekBUTTON_PUSH: - widget = gtk_button_new(); + widget = gtk_button_new_with_label(""); gtk_button_set_use_underline(GTK_BUTTON(widget), TRUE); g_signal_connect(widget, "draw", G_CALLBACK(i_OnPushDraw), button); focus_widget = widget; @@ -186,19 +312,16 @@ OSButton *osbutton_create(const uint32_t flags) case ekBUTTON_FLAT: widget = (GtkWidget *)gtk_tool_button_new(NULL, NULL); focus_widget = gtk_bin_get_child(GTK_BIN(widget)); - i_flat_button_zero_padding(focus_widget); break; case ekBUTTON_FLATGLE: widget = (GtkWidget *)gtk_toggle_tool_button_new(); focus_widget = gtk_bin_get_child(GTK_BIN(widget)); - i_flat_button_zero_padding(focus_widget); break; case ekBUTTON_RADIO: widget = gtk_radio_button_new_with_label(NULL, ""); gtk_button_set_use_underline(GTK_BUTTON(widget), TRUE); - _oscontrol_widget_set_css(widget, "radiobutton {padding-left:0px;padding-right:0px;padding-top:0px;padding-bottom:0px;}"); button->radio = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(widget), ""); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button->radio), TRUE); focus_widget = widget; @@ -206,21 +329,38 @@ OSButton *osbutton_create(const uint32_t flags) case ekBUTTON_CHECK2: case ekBUTTON_CHECK3: - widget = gtk_check_button_new(); + widget = gtk_check_button_new_with_label(""); gtk_button_set_use_underline(GTK_BUTTON(widget), TRUE); - _oscontrol_widget_set_css(widget, "checkbutton {padding-left:0px;padding-right:0px;padding-top:0px;padding-bottom:0px;}"); focus_widget = widget; break; } + cssobj = i_css_obj(button->flags); g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(i_OnClick), (gpointer)button); _oscontrol_init(&button->control, ekGUI_TYPE_BUTTON, widget, focus_widget, FALSE); + /* + * Set CSS padding to 0. Like Windows, the button padding in the difference + * between button frame and content size. + */ + button->vpadding = kBUTTON_VPADDING; + button->hpadding = kBUTTON_HPADDING; + _oscontrol_update_css_padding(button->control.widget, cssobj, 0, 0, &button->css_padding); + + /* + * We set the button font infinitely small for two reasons. + * 1) That the button text does not influence its minimum size, allowing to create buttons of any size. + * 2) Ability to assign text with mnemonics, maintain the GTK shortcuts support. + */ if (osbutton_text_allowed(flags) == TRUE) { - const char_t *cssbut = osglobals_css_button(); + Font *fake_font = font_system(0, 0); + _oscontrol_update_css_font(button->control.widget, cssobj, fake_font, &button->fake_font, &button->css_font); + font_destroy(&fake_font); + /* The real font and text for button */ + button->text = str_c(""); + button->markup = str_c(""); button->font = osgui_create_default_font(); - _oscontrol_widget_font(button->control.widget, cssbut, button->font, &button->pfont); } g_signal_connect(G_OBJECT(widget), "button-press-event", G_CALLBACK(i_OnPressed), (gpointer)button); @@ -236,7 +376,6 @@ void osbutton_destroy(OSButton **button) cassert_no_null(button); cassert_no_null(*button); - listener_destroy(&(*button)->OnClick); if ((*button)->radio != NULL) { /* @@ -249,7 +388,14 @@ void osbutton_destroy(OSButton **button) g_object_unref((*button)->radio); } + listener_destroy(&(*button)->OnClick); + str_destopt(&(*button)->text); + str_destopt(&(*button)->markup); + ptr_destopt(image_destroy, &(*button)->image, Image); + ptr_destopt(font_destroy, &(*button)->fake_font, Font); ptr_destopt(font_destroy, &(*button)->font, Font); + _oscontrol_destroy_css_provider(&(*button)->css_padding); + _oscontrol_destroy_css_provider(&(*button)->css_font); _oscontrol_destroy(*(OSControl **)button); heap_delete(button, OSButton); } @@ -264,13 +410,49 @@ void osbutton_OnClick(OSButton *button, Listener *listener) /*---------------------------------------------------------------------------*/ +static void i_update_text_extents(OSButton *button) +{ + /* Text measure for future bounds and positioning */ + cassert_no_null(button); + font_extents(button->font, tc(button->text), -1.f, &button->textwidth, &button->textheight); + button->textwidth = bmath_ceilf(button->textwidth); + button->textheight = bmath_ceilf(button->textheight); +} + +/*---------------------------------------------------------------------------*/ + void osbutton_text(OSButton *button, const char_t *text) { - char_t tbuff[256]; + /* + * We need to manage three kind of text strings: + * 1) 'shortcut': translate incomming 'text' NAppGUI '&' to GTK '_' mnemonics. + * 2) 'markup': pango markup '' to underline the shortcut. + * 3) 'plain': Without any markup or mnemonic for text measure. + */ + char_t shortcut[256], markup[256], plain[256]; + uint32_t pos = UINT32_MAX; cassert_no_null(button); cassert(osbutton_text_allowed(button->flags) == TRUE); - _osgui_underline_gtk_text(text, tbuff, sizeof(tbuff)); - gtk_button_set_label(GTK_BUTTON(button->control.widget), tbuff); + pos = _osgui_underline_gtk_text(text, shortcut, sizeof(shortcut)); + _osgui_underline_markup(shortcut, pos, markup, sizeof(markup)); + _osgui_underline_plain(shortcut, pos, plain, sizeof(plain)); + str_upd(&button->text, plain); + str_upd(&button->markup, markup); + + /* + * The button inner label is a fake with '0' font size (invisible). + * The use it just for GTK automatic manage of shortcuts. + * The button 'real' text will be rendered in 'i_OnLabelDraw' + */ + { + GtkWidget *label = NULL; + gtk_button_set_label(GTK_BUTTON(button->control.widget), shortcut); + label = i_get_gtk_label(button->control.widget); + if (label != NULL) + g_signal_connect(G_OBJECT(label), "draw", G_CALLBACK(i_OnLabelDraw), button); + } + + i_update_text_extents(button); } /*---------------------------------------------------------------------------*/ @@ -285,11 +467,13 @@ void osbutton_tooltip(OSButton *button, const char_t *text) void osbutton_font(OSButton *button, const Font *font) { - const char_t *cssbut = osglobals_css_button(); cassert_no_null(button); - cassert(osbutton_text_allowed(button->flags) == TRUE); - _oscontrol_widget_remove_provider(button->control.widget, button->pfont); - _oscontrol_widget_font(button->control.widget, cssbut, font, &button->pfont); + if (font_equals(font, button->font) == FALSE) + { + font_destroy(&button->font); + button->font = font_copy(font); + i_update_text_extents(button); + } } /*---------------------------------------------------------------------------*/ @@ -307,34 +491,23 @@ void osbutton_image(OSButton *button, const Image *image) { cassert_no_null(button); cassert_no_null(image); + cassert(osbutton_image_allowed(button->flags) == TRUE); + if (button->image != NULL) + image_destroy(&button->image); + button->image = image_copy(image); + switch (button_get_type(button->flags)) { case ekBUTTON_FLAT: case ekBUTTON_FLATGLE: { - const char_t *icon_name = _osgui_register_icon(image); + const char_t *icon_name = _osgui_register_icon(button->image); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(button->control.widget), icon_name); break; } + /* In push buttons, image will be rendered in custom 'i_OnLabelDraw()' */ case ekBUTTON_PUSH: - if (image != NULL) - { - GdkPixbuf *pixbuf = (GdkPixbuf *)image_native(image); - GtkWidget *widget = gtk_image_new_from_pixbuf(pixbuf); - gtk_button_set_image(GTK_BUTTON(button->control.widget), widget); -#if GTK_CHECK_VERSION(3, 6, 0) - gtk_button_set_always_show_image(GTK_BUTTON(button->control.widget), TRUE); -#endif - /* g_object_unref(widget); */ - } - else - { - gtk_button_set_image(GTK_BUTTON(button->control.widget), NULL); -#if GTK_CHECK_VERSION(3, 6, 0) - gtk_button_set_always_show_image(GTK_BUTTON(button->control.widget), FALSE); -#endif - } break; cassert_default(); @@ -396,49 +569,30 @@ gui_state_t osbutton_get_state(const OSButton *button) /*---------------------------------------------------------------------------*/ -#if defined __ASSERTS__ - -static ___INLINE const char *i_button_label(const OSButton *button) +void osbutton_vpadding(OSButton *button, const real32_t padding) { - const char *label = NULL; cassert_no_null(button); - label = gtk_button_get_label(GTK_BUTTON(button->control.widget)); - if (label != NULL) - return label; - else - return ""; -} - -/*---------------------------------------------------------------------------*/ - -static ___INLINE bool_t i_equal_button_label(const OSButton *button, const char_t *text) -{ - char_t buff[256]; - _osgui_underline_gtk_text(text, buff, sizeof(buff)); - return str_equ_c(buff, i_button_label(button)); + if (button_get_type(button->flags) == ekBUTTON_PUSH) + button->vpadding = padding >= 0 ? (uint32_t)padding : kBUTTON_VPADDING; } -#endif +#if defined __ASSERTS__ /*---------------------------------------------------------------------------*/ -void osbutton_vpadding(OSButton *button, const real32_t padding) +static ___INLINE bool_t i_equal_button_text(const OSButton *button, const char_t *text) { + char_t shortcut[256], plain[256]; + uint32_t pos = UINT32_MAX; cassert_no_null(button); - if (button_get_type(button->flags) == ekBUTTON_PUSH) - { - const char_t *cssbut = osglobals_css_button(); - uint32_t mpad = (uint32_t)((padding / 2) + .5f); - char_t css[256]; -#if GTK_CHECK_VERSION(3, 22, 0) - bstd_sprintf(css, sizeof(css), "%s {padding-top:%dpx;padding-bottom:%dpx;padding-left:4px;padding-right:4px;min-height:0}", cssbut, mpad, mpad); -#else - bstd_sprintf(css, sizeof(css), "%s {padding-top:%dpx;padding-bottom:%dpx;padding-left:4px;padding-right:4px}", cssbut, mpad, mpad); -#endif - _oscontrol_widget_set_css(button->control.widget, css); - } + cassert(osbutton_text_allowed(button->flags) == TRUE); + pos = _osgui_underline_gtk_text(text, shortcut, sizeof(shortcut)); + _osgui_underline_plain(shortcut, pos, plain, sizeof(plain)); + return str_equ_c(plain, tc(button->text)); } +#endif + /*---------------------------------------------------------------------------*/ void osbutton_bounds(const OSButton *button, const char_t *text, const real32_t refwidth, const real32_t refheight, real32_t *width, real32_t *height) @@ -451,12 +605,28 @@ void osbutton_bounds(const OSButton *button, const char_t *text, const real32_t { case ekBUTTON_PUSH: { - GtkRequisition s; - cassert_unref(i_equal_button_label(button, text) == TRUE, text); - gtk_widget_set_size_request(button->control.widget, -1, -1); - gtk_widget_get_preferred_size(button->control.widget, &s, NULL); - *width = (real32_t)s.width; - *height = (real32_t)s.height; + cassert_unref(i_equal_button_text(button, text) == TRUE, text); + cassert(button->vpadding != UINT32_MAX); + cassert(button->hpadding != UINT32_MAX); + + *width = button->textwidth + i_PUSHBUTTON_EXTRAWIDTH; + + /* Image width */ + if (refwidth > 0.f) + { + *width += refwidth; + *width += (real32_t)kBUTTON_IMAGE_SEP; + } + + *width += (real32_t)button->hpadding; + + /* Image height */ + if (refheight > button->textheight) + *height = refheight; + else + *height = button->textheight; + + *height += (real32_t)button->vpadding; break; } @@ -464,11 +634,16 @@ void osbutton_bounds(const OSButton *button, const char_t *text, const real32_t case ekBUTTON_CHECK3: case ekBUTTON_RADIO: { - GtkRequisition s; - cassert_unref(i_equal_button_label(button, text) == TRUE, text); - gtk_widget_get_preferred_size(button->control.widget, &s, NULL); - *width = (real32_t)s.width; - *height = (real32_t)s.height; + cassert_unref(i_equal_button_text(button, text) == TRUE, text); + cassert(button->vpadding != UINT32_MAX); + cassert(button->hpadding != UINT32_MAX); + *width = button->textwidth + i_PUSHBUTTON_EXTRAWIDTH; + *width += (real32_t)osglobals_check_width(); + *width += kCHECKBOX_IMAGE_SEP; + *height = (real32_t)osglobals_check_height(); + if (button->textheight > *height) + *height = button->textheight; + *height += i_CHECKBOX_EXTRAHEIGHT; break; } diff --git a/src/osgui/gtk/oscombo.c b/src/osgui/gtk/oscombo.c index 4c11d977..aa33557d 100644 --- a/src/osgui/gtk/oscombo.c +++ b/src/osgui/gtk/oscombo.c @@ -15,9 +15,11 @@ #include "osgui.inl" #include "osgui_gtk.inl" #include "oscombo_gtk.inl" +#include "osglobals_gtk.inl" #include "oscontrol_gtk.inl" #include "ospanel_gtk.inl" #include "oswindow_gtk.inl" +#include #include #include #include @@ -38,9 +40,10 @@ struct _oscombo_t GtkWidget *button; GtkCellRenderer *imgcell; GtkCellRenderer *txtcell; - GtkCssProvider *color; - GtkCssProvider *bgcolor; - GtkCssProvider *font; + Font *font; + GtkCssProvider *css_font; + GtkCssProvider *css_color; + GtkCssProvider *css_bgcolor; bool_t launch_event; color_t ccolor; int32_t select_start; @@ -160,7 +163,9 @@ OSCombo *oscombo_create(const uint32_t flags) Font *font = osgui_create_default_font(); GtkWidget *widget = gtk_combo_box_new_with_entry(); GtkWidget *entry = gtk_bin_get_child(GTK_BIN(widget)); + const char_t *cssobj = osglobals_css_entry(); cassert_unref(flags == ekCOMBO_FLAG, flags); + cassert(GTK_IS_ENTRY(entry) == TRUE); combo->select_start = INT32_MAX; combo->select_end = INT32_MAX; gtk_entry_set_width_chars(GTK_ENTRY(entry), 0); @@ -204,7 +209,7 @@ OSCombo *oscombo_create(const uint32_t flags) gtk_widget_show(combo->combo); _oscontrol_init(&combo->control, ekGUI_TYPE_COMBOBOX, widget, entry, TRUE); - _oscontrol_widget_font(entry, "entry", font, &combo->font); + _oscontrol_update_css_font(entry, cssobj, font, &combo->font, &combo->css_font); combo->fsize = (uint32_t)(font_size(font) + 2.5f); font_destroy(&font); combo->texts = arrpt_create(String); @@ -234,6 +239,10 @@ void oscombo_destroy(OSCombo **combo) listener_destroy(&(*combo)->OnSelect); arrpt_destroy(&(*combo)->texts, str_destroy, String); arrpt_destroy(&(*combo)->images, i_img_dest, Image); + font_destroy(&(*combo)->font); + _oscontrol_destroy_css_provider(&(*combo)->css_font); + _oscontrol_destroy_css_provider(&(*combo)->css_color); + _oscontrol_destroy_css_provider(&(*combo)->css_bgcolor); _oscontrol_destroy(*(OSControl **)combo); heap_delete(combo, OSCombo); } @@ -298,14 +307,17 @@ void oscombo_tooltip(OSCombo *combo, const char_t *text) void oscombo_font(OSCombo *combo, const Font *font) { - GtkWidget *entry; cassert_no_null(combo); cassert(GTK_IS_EVENT_BOX(combo->control.widget)); cassert(GTK_IS_COMBO_BOX(combo->combo)); - entry = gtk_bin_get_child(GTK_BIN(combo->combo)); - _oscontrol_widget_remove_provider(entry, combo->font); - _oscontrol_widget_font(entry, "entry", font, &combo->font); - combo->fsize = (uint32_t)(font_size(font) + 2.5f); + if (font_equals(combo->font, font) == FALSE) + { + GtkWidget *entry = gtk_bin_get_child(GTK_BIN(combo->combo)); + const char_t *cssobj = osglobals_css_entry(); + cassert(GTK_IS_ENTRY(entry) == TRUE); + _oscontrol_update_css_font(entry, cssobj, font, &combo->font, &combo->css_font); + combo->fsize = (uint32_t)(font_size(font) + 2.5f); + } } /*---------------------------------------------------------------------------*/ @@ -330,19 +342,15 @@ void oscombo_passmode(OSCombo *combo, const bool_t passmode) static void i_set_color(OSCombo *combo, const color_t color) { - GtkWidget *entry; + const char_t *cssobj = NULL; + GtkWidget *entry = NULL; cassert_no_null(combo); cassert(GTK_IS_EVENT_BOX(combo->control.widget)); cassert(GTK_IS_COMBO_BOX(combo->combo)); + cssobj = osglobals_css_entry(); entry = gtk_bin_get_child(GTK_BIN(combo->combo)); - if (combo->color != NULL) - { - _oscontrol_widget_remove_provider(entry, combo->color); - combo->color = NULL; - } - - if (color != 0) - _oscontrol_widget_color(entry, "entry", color, &combo->color); + cassert(GTK_IS_ENTRY(entry) == TRUE); + _oscontrol_update_css_color(entry, cssobj, color, &combo->css_color); } /*---------------------------------------------------------------------------*/ @@ -357,19 +365,12 @@ void oscombo_color(OSCombo *combo, const color_t color) void oscombo_bgcolor(OSCombo *combo, const color_t color) { - GtkWidget *entry; + /* TODO: bgcolor from osedit */ cassert_no_null(combo); cassert(GTK_IS_EVENT_BOX(combo->control.widget)); cassert(GTK_IS_COMBO_BOX(combo->combo)); - entry = gtk_bin_get_child(GTK_BIN(combo->combo)); - if (combo->bgcolor != NULL) - { - _oscontrol_widget_remove_provider(entry, combo->bgcolor); - combo->bgcolor = NULL; - } - - if (color != UINT32_MAX) - _oscontrol_widget_bg_color(entry, "entry", color, &combo->bgcolor); + unref(combo); + unref(color); } /*---------------------------------------------------------------------------*/ diff --git a/src/osgui/gtk/oscontrol.c b/src/osgui/gtk/oscontrol.c index 0865384a..3bf88b84 100644 --- a/src/osgui/gtk/oscontrol.c +++ b/src/osgui/gtk/oscontrol.c @@ -168,39 +168,6 @@ void _oscontrol_set_halign(OSControl *control, const align_t align) /*---------------------------------------------------------------------------*/ -void _oscontrol_text_bounds(const OSControl *control, PangoLayout *layout, const char_t *text, const Font *font, const real32_t refwidth, real32_t *width, real32_t *height) -{ - int w, h; - - if (layout == NULL) - { - const PangoFontDescription *fdesc = (PangoFontDescription *)font_native(font); - pango_layout_set_font_description(kPANGO_LAYOUT, fdesc); - pango_layout_set_wrap(kPANGO_LAYOUT, PANGO_WRAP_WORD); - pango_layout_set_ellipsize(kPANGO_LAYOUT, PANGO_ELLIPSIZE_NONE); - pango_layout_set_alignment(kPANGO_LAYOUT, PANGO_ALIGN_CENTER); - pango_layout_set_width(kPANGO_LAYOUT, refwidth > 0.f ? (int)(refwidth * (real32_t)PANGO_SCALE) : -1); - pango_layout_set_text(kPANGO_LAYOUT, (const char *)text, -1); - pango_layout_get_pixel_size(kPANGO_LAYOUT, &w, &h); - } - else - { - pango_layout_set_width(layout, refwidth > 0.f ? (int)(refwidth * (real32_t)PANGO_SCALE) : -1); - pango_layout_set_text(layout, (const char *)text, -1); - pango_layout_get_pixel_size(layout, &w, &h); - } - - *width = (real32_t)w; - *height = (real32_t)h; - unref(control); - /* - data.layout = kPANGO_LAYOUT; - osgui_text_bounds(&data, text, refwidth, width, height); - */ -} - -/*---------------------------------------------------------------------------*/ - void _oscontrol_set_visible(OSControl *control, const bool_t visible) { cassert_no_null(control); @@ -247,7 +214,20 @@ void _oscontrol_get_origin(const OSControl *control, real32_t *x, real32_t *y) void _oscontrol_get_size(const OSControl *control, real32_t *width, real32_t *height) { cassert_no_null(control); - _oscontrol_widget_size(control->widget, width, height); + _oscontrol_widget_get_size(control->widget, width, height); +} + +/*---------------------------------------------------------------------------*/ + +void _oscontrol_widget_get_size(const GtkWidget *widget, real32_t *width, real32_t *height) +{ + GtkAllocation alloc; + cassert_no_null(widget); + cassert_no_null(width); + cassert_no_null(height); + i_widget_allocation(cast(widget, GtkWidget), &alloc); + *width = (real32_t)alloc.width; + *height = (real32_t)alloc.height; } /*---------------------------------------------------------------------------*/ @@ -341,121 +321,67 @@ GtkWidget *_oscontrol_get_child(GtkContainer *container, const uint32_t index) /*---------------------------------------------------------------------------*/ -bool_t _oscontrol_widget_mouse_over(GtkWidget *widget, GdkEvent *event) +GtkCssProvider *_oscontrol_css_provider(const char_t *css) { - double x, y; - if (gdk_event_get_coords(event, &x, &y) == TRUE) - { - GtkAllocation alloc; - gint event_x = (gint)x; - gint event_y = (gint)y; - i_widget_allocation(widget, &alloc); - - if (event_x >= alloc.x && event_x <= alloc.x + alloc.width && event_y >= alloc.y && event_y <= alloc.y + alloc.height) - { - return TRUE; - } - } - - return FALSE; + GtkCssProvider *p = gtk_css_provider_new(); + gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(p), (gchar *)css, -1, NULL); + return p; } /*---------------------------------------------------------------------------*/ -bool_t _oscontrol_widget_mouse_over_right(GtkWidget *widget, GdkEvent *event, gint right_px) +void _oscontrol_destroy_css_provider(GtkCssProvider **prov) { - double x, y; - if (gdk_event_get_coords(event, &x, &y) == TRUE) + cassert_no_null(prov); + if (*prov != NULL) { - GtkAllocation alloc; - gint event_x = (gint)x; - gint event_y = (gint)y; - i_widget_allocation(widget, &alloc); - - if (event_x >= alloc.x + alloc.width - right_px && event_x <= alloc.x + alloc.width && event_y >= alloc.y && event_y <= alloc.y + alloc.height) - { - return TRUE; - } + g_object_unref(*prov); + *prov = NULL; } - - return FALSE; -} - -/*---------------------------------------------------------------------------*/ - -void _oscontrol_widget_set_css(GtkWidget *widget, const char_t *css) -{ - _oscontrol_widget_set_provider(widget, css, NULL); -} - -/*---------------------------------------------------------------------------*/ - -void _oscontrol_widget_set_provider(GtkWidget *widget, const char_t *css, GtkCssProvider **css_prov) -{ - GtkCssProvider *p = gtk_css_provider_new(); - GtkStyleContext *c = gtk_widget_get_style_context(widget); - gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(p), (gchar *)css, -1, NULL); - gtk_style_context_add_provider(c, GTK_STYLE_PROVIDER(p), GTK_STYLE_PROVIDER_PRIORITY_USER); - g_object_unref(p); - ptr_assign(css_prov, p); } /*---------------------------------------------------------------------------*/ -void _oscontrol_widget_add_provider(GtkWidget *widget, GtkCssProvider *css_prov) +void _oscontrol_add_css_provider(GtkWidget *widget, GtkCssProvider *prov) { GtkStyleContext *c = gtk_widget_get_style_context(widget); - cassert_no_null(css_prov); - gtk_style_context_add_provider(c, GTK_STYLE_PROVIDER(css_prov), GTK_STYLE_PROVIDER_PRIORITY_USER); + gtk_style_context_add_provider(c, GTK_STYLE_PROVIDER(prov), GTK_STYLE_PROVIDER_PRIORITY_USER); } /*---------------------------------------------------------------------------*/ -void _oscontrol_widget_remove_provider(GtkWidget *widget, GtkCssProvider *css_prov) +void _oscontrol_remove_css_provider(GtkWidget *widget, GtkCssProvider *prov) { GtkStyleContext *c = gtk_widget_get_style_context(widget); - cassert_no_null(css_prov); - gtk_style_context_remove_provider(c, GTK_STYLE_PROVIDER(css_prov)); + gtk_style_context_remove_provider(c, GTK_STYLE_PROVIDER(prov)); } /*---------------------------------------------------------------------------*/ -void _oscontrol_widget_color(GtkWidget *widget, const char_t *css_type, const color_t color, GtkCssProvider **css_provider) +static void i_update_css_provider(GtkWidget *widget, const char_t *css, GtkCssProvider **prov) { - char_t html[16]; - String *css; - color_to_html(color, html, sizeof(html)); - css = str_printf("%s {color:%s;}", css_type, html); - _oscontrol_widget_set_provider(widget, tc(css), css_provider); - str_destroy(&css); -} - -/*---------------------------------------------------------------------------*/ + cassert_no_null(prov); + if (*prov != NULL) + { + _oscontrol_remove_css_provider(widget, *prov); + g_object_unref(*prov); + *prov = NULL; + } -void _oscontrol_widget_bg_color(GtkWidget *widget, const char_t *css_type, const color_t color, GtkCssProvider **css_provider) -{ - char_t html[16]; - String *css; - color_to_html(color, html, sizeof(html)); - css = str_printf("%s {background-color:%s;background-image:none;}", css_type, html); - _oscontrol_widget_set_provider(widget, tc(css), css_provider); - str_destroy(&css); + if (css != NULL) + { + *prov = _oscontrol_css_provider(css); + _oscontrol_add_css_provider(widget, *prov); + } } /*---------------------------------------------------------------------------*/ -uint32_t _oscontrol_widget_font_size(GtkWidget *widget) +void _oscontrol_fixed_css_provider(GtkWidget *widget, const char_t *css) { - PangoFontDescription *fdesc; - GtkStyleContext *ctx = gtk_widget_get_style_context(widget); - PangoFontMap *fontmap = pango_cairo_font_map_get_default(); - real32_t dpi = (real32_t)pango_cairo_font_map_get_resolution((PangoCairoFontMap *)fontmap); - real32_t fsize = 0; - gtk_style_context_get(ctx, GTK_STATE_FLAG_NORMAL, "font", &fdesc, NULL); - fsize = (real32_t)pango_font_description_get_size(fdesc); - pango_font_description_free(fdesc); - fsize = fsize * (dpi / 72.f) / (real32_t)PANGO_SCALE; - return (uint32_t)fsize; + GtkCssProvider *prov = _oscontrol_css_provider(css); + _oscontrol_add_css_provider(widget, prov); + g_object_unref(prov); } /*---------------------------------------------------------------------------*/ @@ -476,7 +402,7 @@ static real32_t i_font_pt(const real32_t fsize, const uint32_t fstyle) /*---------------------------------------------------------------------------*/ -void _oscontrol_widget_font_desc(GtkWidget *widget, const char_t *css_type, const char_t *ffamily, const real32_t fsize, const uint32_t fstyle, GtkCssProvider **css_prov) +static String *i_css_font_desc(const char_t *cssobj, const char_t *ffamily, const real32_t fsize, const uint32_t fstyle) { real32_t ptsize = i_font_pt(fsize, fstyle); const char *italic = (fstyle & ekFITALIC) ? "italic" : "normal"; @@ -484,7 +410,7 @@ void _oscontrol_widget_font_desc(GtkWidget *widget, const char_t *css_type, cons Stream *stm = stm_memory(256); String *css = NULL; - stm_printf(stm, "%s { ", css_type); + stm_printf(stm, "%s { ", cssobj); stm_printf(stm, "font-family: \"%s\"; ", ffamily); #if GTK_CHECK_VERSION(3, 22, 0) @@ -498,6 +424,9 @@ void _oscontrol_widget_font_desc(GtkWidget *widget, const char_t *css_type, cons stm_printf(stm, "font-style: %s; ", italic); stm_printf(stm, "font-weight: %s; ", bold); + /* https://discourse.gnome.org/t/is-it-possible-to-shrink-text-in-gtkentry/21213 */ + /* stm_printf(stm, "font-stretch: expanded; "); */ + #if GTK_CHECK_VERSION(3, 22, 0) if (fstyle & ekFUNDERLINE) { @@ -518,33 +447,182 @@ void _oscontrol_widget_font_desc(GtkWidget *widget, const char_t *css_type, cons stm_printf(stm, "}"); css = stm_str(stm); - _oscontrol_widget_set_provider(widget, tc(css), css_prov); - str_destroy(&css); stm_close(&stm); + return css; } /*---------------------------------------------------------------------------*/ -void _oscontrol_widget_font(GtkWidget *widget, const char_t *css_type, const Font *font, GtkCssProvider **css_prov) +void _oscontrol_update_css_color(GtkWidget *widget, const char_t *cssobj, const color_t color, GtkCssProvider **prov) { - PangoFontDescription *fdesc = (PangoFontDescription *)font_native(font); - const char *ffamily = pango_font_description_get_family(fdesc); - real32_t fsize = font_size(font); - uint32_t fstyle = font_style(font); - _oscontrol_widget_font_desc(widget, css_type, ffamily, fsize, fstyle, css_prov); + if (color != kCOLOR_DEFAULT) + { + char_t html[16]; + String *css = NULL; + color_to_html(color, html, sizeof(html)); + css = str_printf("%s {color:%s;}", cssobj, html); + i_update_css_provider(widget, tc(css), prov); + str_destroy(&css); + } + else + { + i_update_css_provider(widget, NULL, prov); + } } /*---------------------------------------------------------------------------*/ -void _oscontrol_widget_size(GtkWidget *widget, real32_t *width, real32_t *height) +void _oscontrol_update_css_bgcolor(GtkWidget *widget, const char_t *cssobj, const color_t color, GtkCssProvider **prov) { - GtkAllocation alloc; - cassert_no_null(widget); - cassert_no_null(width); - cassert_no_null(height); - i_widget_allocation(widget, &alloc); - *width = (real32_t)alloc.width; - *height = (real32_t)alloc.height; + if (color != kCOLOR_DEFAULT) + { + char_t html[16]; + String *css = NULL; + color_to_html(color, html, sizeof(html)); + /* background-image:none affects to Edit multiline background */ + /* css = str_printf("%s {background:%s;background-image:none;} %s:selected {background-color:@selected_bg_color;background-image:none;}", cssobj, html, cssobj); */ + css = str_printf("%s {background:%s;} %s:selected {background-color:@selected_bg_color;}", cssobj, html, cssobj); + i_update_css_provider(widget, tc(css), prov); + str_destroy(&css); + } + else + { + i_update_css_provider(widget, NULL, prov); + } +} + +/*---------------------------------------------------------------------------*/ + +void _oscontrol_update_css_font(GtkWidget *widget, const char_t *cssobj, const Font *font, Font **cfont, GtkCssProvider **prov) +{ + cassert_no_null(cfont); + cassert_no_null(prov); + if (*cfont == NULL || font_equals(*cfont, font) == FALSE) + { + /* Remove the css provider */ + if (*prov != NULL) + { + _oscontrol_remove_css_provider(widget, *prov); + g_object_unref(*prov); + *prov = NULL; + } + + if (*cfont != NULL) + font_destroy(cfont); + + *cfont = font_copy(font); + + { + PangoFontDescription *fdesc = (PangoFontDescription *)font_native(*cfont); + const char *ffamily = pango_font_description_get_family(fdesc); + real32_t fsize = font_size(*cfont); + uint32_t fstyle = font_style(*cfont); + String *css = i_css_font_desc(cssobj, ffamily, fsize, fstyle); + *prov = _oscontrol_css_provider(tc(css)); + _oscontrol_add_css_provider(widget, *prov); + str_destroy(&css); + } + } +} + +/*---------------------------------------------------------------------------*/ + +void _oscontrol_update_css_font_desc(GtkWidget *widget, const char_t *cssobj, const char_t *ffamily, const real32_t fsize, const uint32_t fstyle, GtkCssProvider **prov) +{ + String *css = i_css_font_desc(cssobj, ffamily, fsize, fstyle); + i_update_css_provider(widget, tc(css), prov); + str_destroy(&css); +} + +/*---------------------------------------------------------------------------*/ + +void _oscontrol_update_css_padding(GtkWidget *widget, const char_t *cssobj, const uint32_t vpadding, const uint32_t hpadding, GtkCssProvider **prov) +{ + char_t css[256]; + css[0] = 0; + + /* Remove padding means use the Gtk theme default padding */ + if (vpadding != UINT32_MAX || hpadding != UINT32_MAX) + { + char_t vpad[64]; + char_t hpad[64]; + vpad[0] = 0; + hpad[0] = 0; + + if (vpadding != UINT32_MAX) + { +#if GTK_CHECK_VERSION(3, 22, 0) + bstd_sprintf(vpad, sizeof(vpad), "padding-top:%dpx;padding-bottom:%dpx;min-height:0;", vpadding / 2, vpadding / 2); +#else + bstd_sprintf(vpad, sizeof(vpad), "padding-top:%dpx;padding-bottom:%dpx;", vpadding / 2, vpadding / 2); +#endif + } + + if (hpadding != UINT32_MAX) + bstd_sprintf(hpad, sizeof(hpad), "padding-left:%dpx;padding-right:%dpx;", hpadding / 2, hpadding / 2); + + bstd_sprintf(css, sizeof(css), "%s {%s %s}", cssobj, vpad, hpad); + } + + i_update_css_provider(widget, css[0] != 0 ? css : NULL, prov); +} + +/*---------------------------------------------------------------------------*/ + +uint32_t _oscontrol_widget_font_size(GtkWidget *widget) +{ + PangoFontDescription *fdesc; + GtkStyleContext *ctx = gtk_widget_get_style_context(widget); + PangoFontMap *fontmap = pango_cairo_font_map_get_default(); + real32_t dpi = (real32_t)pango_cairo_font_map_get_resolution((PangoCairoFontMap *)fontmap); + real32_t fsize = 0; + gtk_style_context_get(ctx, GTK_STATE_FLAG_NORMAL, "font", &fdesc, NULL); + fsize = (real32_t)pango_font_description_get_size(fdesc); + pango_font_description_free(fdesc); + fsize = fsize * (dpi / 72.f) / (real32_t)PANGO_SCALE; + return (uint32_t)fsize; +} + +/*---------------------------------------------------------------------------*/ + +bool_t _oscontrol_widget_mouse_over(GtkWidget *widget, GdkEvent *event) +{ + double x, y; + if (gdk_event_get_coords(event, &x, &y) == TRUE) + { + GtkAllocation alloc; + gint event_x = (gint)x; + gint event_y = (gint)y; + i_widget_allocation(widget, &alloc); + + if (event_x >= alloc.x && event_x <= alloc.x + alloc.width && event_y >= alloc.y && event_y <= alloc.y + alloc.height) + { + return TRUE; + } + } + + return FALSE; +} + +/*---------------------------------------------------------------------------*/ + +bool_t _oscontrol_widget_mouse_over_right(GtkWidget *widget, GdkEvent *event, gint right_px) +{ + double x, y; + if (gdk_event_get_coords(event, &x, &y) == TRUE) + { + GtkAllocation alloc; + gint event_x = (gint)x; + gint event_y = (gint)y; + i_widget_allocation(widget, &alloc); + + if (event_x >= alloc.x + alloc.width - right_px && event_x <= alloc.x + alloc.width && event_y >= alloc.y && event_y <= alloc.y + alloc.height) + { + return TRUE; + } + } + + return FALSE; } /*---------------------------------------------------------------------------*/ @@ -582,15 +660,6 @@ void _oscontrol_to_gdkrgba(const color_t color, GdkRGBA *gdkcolor) /*---------------------------------------------------------------------------*/ -void _oscontrol_to_css_rgb(const color_t color, char_t *css, const uint32_t size) -{ - uint8_t r, g, b; - color_get_rgb(color, &r, &g, &b); - bstd_sprintf(css, size, "rgb(%d,%d,%d)", r, g, b); -} - -/*---------------------------------------------------------------------------*/ - GtkJustification _oscontrol_justification(const align_t align) { switch (align) diff --git a/src/osgui/gtk/oscontrol_gtk.inl b/src/osgui/gtk/oscontrol_gtk.inl index 76b3ae28..09a852e4 100644 --- a/src/osgui/gtk/oscontrol_gtk.inl +++ b/src/osgui/gtk/oscontrol_gtk.inl @@ -20,8 +20,6 @@ void _oscontrol_destroy(OSControl *control); void _oscontrol_set_halign(OSControl *control, const align_t align); -void _oscontrol_text_bounds(const OSControl *control, PangoLayout *layout, const char_t *text, const Font *font, const real32_t refwidth, real32_t *width, real32_t *height); - void _oscontrol_set_visible(OSControl *control, const bool_t visible); void _oscontrol_set_enabled(OSControl *control, const bool_t enabled); @@ -30,6 +28,8 @@ void _oscontrol_get_origin(const OSControl *control, real32_t *x, real32_t *y); void _oscontrol_get_size(const OSControl *control, real32_t *width, real32_t *height); +void _oscontrol_widget_get_size(const GtkWidget *widget, real32_t *width, real32_t *height); + void _oscontrol_set_frame(OSControl *control, const real32_t x, const real32_t y, const real32_t width, const real32_t height); void _oscontrol_attach_to_parent(OSControl *control, GtkWidget *parent_widget); @@ -42,25 +42,27 @@ uint32_t _oscontrol_num_children(GtkContainer *container); GtkWidget *_oscontrol_get_child(GtkContainer *container, const uint32_t index); -void _oscontrol_widget_set_css(GtkWidget *widget, const char_t *css); +GtkCssProvider *_oscontrol_css_provider(const char_t *css); -void _oscontrol_widget_set_provider(GtkWidget *widget, const char_t *css, GtkCssProvider **css_prov); +void _oscontrol_destroy_css_provider(GtkCssProvider **prov); -void _oscontrol_widget_add_provider(GtkWidget *widget, GtkCssProvider *css_prov); +void _oscontrol_add_css_provider(GtkWidget *widget, GtkCssProvider *prov); -void _oscontrol_widget_remove_provider(GtkWidget *widget, GtkCssProvider *css_prov); +void _oscontrol_remove_css_provider(GtkWidget *widget, GtkCssProvider *prov); -void _oscontrol_widget_color(GtkWidget *widget, const char_t *css_type, const color_t color, GtkCssProvider **css_provider); +void _oscontrol_fixed_css_provider(GtkWidget *widget, const char_t *css); -void _oscontrol_widget_bg_color(GtkWidget *widget, const char_t *css_type, const color_t color, GtkCssProvider **css_provider); +void _oscontrol_update_css_color(GtkWidget *widget, const char_t *cssobj, const color_t color, GtkCssProvider **prov); -uint32_t _oscontrol_widget_font_size(GtkWidget *widget); +void _oscontrol_update_css_bgcolor(GtkWidget *widget, const char_t *cssobj, const color_t color, GtkCssProvider **prov); -void _oscontrol_widget_font_desc(GtkWidget *widget, const char_t *css_type, const char_t *ffamily, const real32_t fsize, const uint32_t fstyle, GtkCssProvider **css_prov); +void _oscontrol_update_css_font(GtkWidget *widget, const char_t *cssobj, const Font *font, Font **cfont, GtkCssProvider **prov); -void _oscontrol_widget_font(GtkWidget *widget, const char_t *css_type, const Font *font, GtkCssProvider **css_prov); +void _oscontrol_update_css_font_desc(GtkWidget *widget, const char_t *cssobj, const char_t *ffamily, const real32_t fsize, const uint32_t fstyle, GtkCssProvider **prov); -void _oscontrol_widget_size(GtkWidget *widget, real32_t *width, real32_t *height); +void _oscontrol_update_css_padding(GtkWidget *widget, const char_t *cssobj, const uint32_t vpadding, const uint32_t hpadding, GtkCssProvider **prov); + +uint32_t _oscontrol_widget_font_size(GtkWidget *widget); bool_t _oscontrol_widget_mouse_over(GtkWidget *widget, GdkEvent *event); @@ -70,8 +72,6 @@ color_t _oscontrol_from_gdkrgba(const GdkRGBA *gdkcolor); void _oscontrol_to_gdkrgba(const color_t color, GdkRGBA *gdkcolor); -void _oscontrol_to_css_rgb(const color_t color, char_t *css, const uint32_t size); - GtkJustification _oscontrol_justification(const align_t align); __END_C diff --git a/src/osgui/gtk/osedit.c b/src/osgui/gtk/osedit.c index 37c154a1..fefa6ab6 100644 --- a/src/osgui/gtk/osedit.c +++ b/src/osgui/gtk/osedit.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -35,16 +36,18 @@ struct _osedit_t { OSControl control; - uint32_t fstyle; - uint32_t fsize; uint32_t fsize_render; bool_t launch_event; bool_t in_validate; + uint32_t vpadding; + uint32_t hpadding; GtkWidget *tview; edit_flag_t flags; - GtkCssProvider *color; - GtkCssProvider *bgcolor; - GtkCssProvider *font; + Font *font; + GtkCssProvider *css_padding; + GtkCssProvider *css_font; + GtkCssProvider *css_color; + GtkCssProvider *css_bgcolor; color_t ccolor; int32_t select_start; int32_t select_end; @@ -104,12 +107,39 @@ static gboolean i_select(OSEdit *edit) static gboolean i_OnDraw(GtkWidget *widget, cairo_t *cr, OSEdit *edit) { - /* It seems gtk_widget_get_preferred_size don't compute font changes - * until draw is made */ - unref(widget); - unref(cr); - edit->fsize_render = edit->fsize; - return FALSE; + real32_t fsize = 0; + real32_t fscale = 1; + cassert_no_null(edit); + fsize = font_size(edit->font); + fscale = font_xscale(edit->font); + edit->fsize_render = (uint32_t)fsize; + + /* + * GtkEntry is not prepared to edit strings with scaled fonts, + * causing precision errors when clicking on a character and limiting + * the width of the PangoLayout. For this reason, scaled fonts are only + * shown when the control does not have keyboard focus. + */ + if (bmath_absf(fscale - 1) >= .01f && gtk_widget_has_focus(widget) == FALSE) + { + int w = gtk_widget_get_allocated_width(widget); + int h = gtk_widget_get_allocated_height(widget); + GtkStyleContext *ctx = osglobals_entry_context(); + PangoLayout *layout = gtk_entry_get_layout(GTK_ENTRY(widget)); + gtk_render_background(ctx, cr, 0, 0, w, h); + gtk_render_frame(ctx, cr, 0, 0, w, h); + cairo_save(cr); + cairo_translate(cr, edit->hpadding / 2, edit->vpadding / 2); + cairo_scale(cr, fscale, 1); + pango_cairo_show_layout(cr, layout); + cairo_restore(cr); + return TRUE; + } + else + { + /* We call to standard draw pipeline */ + return FALSE; + } } /*---------------------------------------------------------------------------*/ @@ -121,18 +151,18 @@ static gboolean i_DrawBackground(GtkWidget *widget, cairo_t *cr, OSEdit *edit) int h = gtk_widget_get_allocated_height(widget); cassert_no_null(edit); - if (edit->bgcolor != NULL) - gtk_style_context_add_provider(ctx, GTK_STYLE_PROVIDER(edit->bgcolor), GTK_STYLE_PROVIDER_PRIORITY_USER); + if (edit->css_bgcolor != NULL) + gtk_style_context_add_provider(ctx, GTK_STYLE_PROVIDER(edit->css_bgcolor), GTK_STYLE_PROVIDER_PRIORITY_USER); gtk_style_context_save(ctx); gtk_style_context_set_state(ctx, gtk_widget_get_state_flags(widget)); gtk_render_background(ctx, cr, 0, 0, w, h); gtk_render_frame(ctx, cr, 0, 0, w, h); gtk_style_context_restore(ctx); - edit->fsize_render = edit->fsize; + edit->fsize_render = (uint32_t)font_size(edit->font); - if (edit->bgcolor != NULL) - gtk_style_context_remove_provider(ctx, GTK_STYLE_PROVIDER(edit->bgcolor)); + if (edit->css_bgcolor != NULL) + gtk_style_context_remove_provider(ctx, GTK_STYLE_PROVIDER(edit->css_bgcolor)); return FALSE; } @@ -280,6 +310,8 @@ OSEdit *osedit_create(const uint32_t flags) Font *font = osgui_create_default_font(); GtkWidget *widget = NULL; edit->flags = flags; + edit->vpadding = kENTRY_VPADDING; + edit->hpadding = kENTRY_HPADDING; edit->select_start = INT32_MAX; edit->select_end = INT32_MAX; @@ -287,10 +319,11 @@ OSEdit *osedit_create(const uint32_t flags) { case ekEDIT_SINGLE: { - const char_t *entry = osglobals_css_entry(); + const char_t *cssobj = osglobals_css_entry(); widget = gtk_entry_new(); gtk_entry_set_width_chars(GTK_ENTRY(widget), 0); - _oscontrol_widget_font(widget, entry, font, &edit->font); + _oscontrol_update_css_padding(widget, cssobj, edit->vpadding, edit->hpadding, &edit->css_padding); + _oscontrol_update_css_font(widget, cssobj, font, &edit->font, &edit->css_font); g_signal_connect(G_OBJECT(widget), "draw", G_CALLBACK(i_OnDraw), (gpointer)edit); g_signal_connect_after(G_OBJECT(widget), "insert-text", G_CALLBACK(i_OnInsert), (gpointer)edit); g_signal_connect_after(G_OBJECT(widget), "delete-text", G_CALLBACK(i_OnDelete), (gpointer)edit); @@ -301,16 +334,14 @@ OSEdit *osedit_create(const uint32_t flags) case ekEDIT_MULTI: { - const char_t *textv = osglobals_css_textview(); + const char_t *cssobj = osglobals_css_textview(); GtkTextBuffer *buffer = NULL; - GtkBorder padding; - String *css1 = NULL; - String *css2 = NULL; edit->tview = gtk_text_view_new(); buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(edit->tview)); g_signal_connect_after(G_OBJECT(buffer), "insert-text", G_CALLBACK(i_OnBufferInsert), (gpointer)edit); g_signal_connect_after(G_OBJECT(buffer), "delete-range", G_CALLBACK(i_OnBufferDelete), (gpointer)edit); - _oscontrol_widget_font(edit->tview, textv, font, &edit->font); + _oscontrol_update_css_padding(edit->tview, cssobj, edit->vpadding, edit->hpadding, &edit->css_padding); + _oscontrol_update_css_font(edit->tview, cssobj, font, &edit->font, &edit->css_font); gtk_text_view_set_accepts_tab(GTK_TEXT_VIEW(edit->tview), FALSE); widget = gtk_scrolled_window_new(NULL, NULL); gtk_container_add(GTK_CONTAINER(widget), edit->tview); @@ -318,29 +349,35 @@ OSEdit *osedit_create(const uint32_t flags) g_signal_connect(G_OBJECT(edit->tview), "button-press-event", G_CALLBACK(i_OnPressed), (gpointer)edit); gtk_widget_show(edit->tview); gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(edit->tview), GTK_WRAP_WORD_CHAR); - osglobals_register_entry(&padding); - css1 = str_printf("%s {background-image:none;background-color:transparent;}", textv); - css2 = str_printf("%s text {background-image:none;background-color:transparent;}", textv); - _oscontrol_widget_set_css(edit->tview, tc(css1)); - _oscontrol_widget_set_css(edit->tview, tc(css2)); - gtk_text_view_set_left_margin(GTK_TEXT_VIEW(edit->tview), padding.left); - gtk_text_view_set_right_margin(GTK_TEXT_VIEW(edit->tview), padding.right); + /* + CSS padding works OK. Check in older GTK3 versions + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(edit->tview), kENTRY_HPADDING / 2); + gtk_text_view_set_right_margin(GTK_TEXT_VIEW(edit->tview), kENTRY_HPADDING / 2); #if GTK_CHECK_VERSION(3, 18, 0) - gtk_text_view_set_top_margin(GTK_TEXT_VIEW(edit->tview), padding.top); - gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW(edit->tview), padding.bottom); + gtk_text_view_set_top_margin(GTK_TEXT_VIEW(edit->tview), kENTRY_VPADDING / 2); + gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW(edit->tview), kENTRY_VPADDING / 2); #endif + */ + + { + String *css = str_printf("%s {background-image:none;background-color:transparent;}", cssobj); + _oscontrol_fixed_css_provider(edit->tview, tc(css)); + str_destroy(&css); + } - str_destroy(&css1); - str_destroy(&css2); + { + String *css = str_printf("%s text {background-image:none;background-color:transparent;}", cssobj); + _oscontrol_fixed_css_provider(edit->tview, tc(css)); + str_destroy(&css); + } break; } cassert_default(); } - edit->fsize = (uint32_t)font_size(font); - edit->fsize_render = edit->fsize; + edit->fsize_render = (uint32_t)font_size(edit->font); edit->ccolor = kCOLOR_TRANSPARENT; edit->launch_event = TRUE; edit->in_validate = FALSE; @@ -368,21 +405,14 @@ void osedit_destroy(OSEdit **edit) g_object_unref((*edit)->tview); */ } - if ((*edit)->bgcolor != NULL) - { - if ((*edit)->tview != NULL) - g_object_unref((*edit)->bgcolor); - (*edit)->bgcolor = NULL; - } - - /* - (*edit)->color Not g_object_unref - (*edit)->font Not g_object_unref - */ - listener_destroy(&(*edit)->OnFilter); listener_destroy(&(*edit)->OnChange); listener_destroy(&(*edit)->OnFocus); + font_destroy(&(*edit)->font); + _oscontrol_destroy_css_provider(&(*edit)->css_padding); + _oscontrol_destroy_css_provider(&(*edit)->css_font); + _oscontrol_destroy_css_provider(&(*edit)->css_color); + _oscontrol_destroy_css_provider(&(*edit)->css_bgcolor); _oscontrol_destroy(*(OSControl **)edit); heap_delete(edit, OSEdit); } @@ -421,19 +451,19 @@ void osedit_text(OSEdit *edit, const char_t *text) if (edit->tview != NULL) { GtkTextBuffer *tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(edit->tview)); + uint32_t fstyle = font_style(edit->font); String *markup = NULL; cassert(edit_get_type(edit->flags) == ekEDIT_MULTI); #if GTK_CHECK_VERSION(3, 22, 0) - - if (edit->fstyle & ekFSTRIKEOUT) + if (fstyle & ekFSTRIKEOUT) { - if (edit->fstyle & ekFUNDERLINE) + if (fstyle & ekFUNDERLINE) markup = str_printf("%s", text); else markup = str_printf("%s", text); } - else if (edit->fstyle & ekFUNDERLINE) + else if (fstyle & ekFUNDERLINE) { markup = str_printf("%s", text); } @@ -477,30 +507,37 @@ void osedit_tooltip(OSEdit *edit, const char_t *text) /*---------------------------------------------------------------------------*/ -void osedit_font(OSEdit *edit, const Font *font) +static void i_cssobjs(OSEdit *edit, const char_t **cssobj, GtkWidget **widget) { cassert_no_null(edit); - edit->fstyle = font_style(font); - cassert(!(edit->fstyle & ekFPOINTS)); - edit->fsize = (uint32_t)font_size(font); if (edit->tview != NULL) { - const char_t *textv = osglobals_css_textview(); cassert(edit_get_type(edit->flags) == ekEDIT_MULTI); - _oscontrol_widget_remove_provider(edit->tview, edit->font); - _oscontrol_widget_font(edit->tview, textv, font, &edit->font); + if (cssobj != NULL) + *cssobj = osglobals_css_textview(); + *widget = edit->tview; } else { - const char_t *entry = osglobals_css_entry(); cassert(edit_get_type(edit->flags) == ekEDIT_SINGLE); - _oscontrol_widget_remove_provider(edit->control.widget, edit->font); - _oscontrol_widget_font(edit->control.widget, entry, font, &edit->font); + if (cssobj != NULL) + *cssobj = osglobals_css_entry(); + *widget = edit->control.widget; } } /*---------------------------------------------------------------------------*/ +void osedit_font(OSEdit *edit, const Font *font) +{ + const char_t *cssobj = NULL; + GtkWidget *widget = NULL; + i_cssobjs(edit, &cssobj, &widget); + _oscontrol_update_css_font(widget, cssobj, font, &edit->font, &edit->css_font); +} + +/*---------------------------------------------------------------------------*/ + void osedit_align(OSEdit *edit, const align_t align) { cassert_no_null(edit); @@ -592,31 +629,10 @@ void osedit_select(OSEdit *edit, const int32_t start, const int32_t end) static void i_set_color(OSEdit *edit, const color_t color) { + const char_t *cssobj = NULL; GtkWidget *widget = NULL; - const char_t *type = NULL; - cassert_no_null(edit); - - if (edit->tview != NULL) - { - cassert(edit_get_type(edit->flags) == ekEDIT_MULTI); - widget = edit->tview; - type = osglobals_css_textview_text(); - } - else - { - cassert(edit_get_type(edit->flags) == ekEDIT_SINGLE); - widget = edit->control.widget; - type = osglobals_css_entry(); - } - - if (edit->color != NULL) - { - _oscontrol_widget_remove_provider(widget, edit->color); - edit->color = NULL; - } - - if (color != kCOLOR_TRANSPARENT) - _oscontrol_widget_color(widget, type, color, &edit->color); + i_cssobjs(edit, &cssobj, &widget); + _oscontrol_update_css_color(widget, cssobj, color, &edit->css_color); } /*---------------------------------------------------------------------------*/ @@ -631,62 +647,22 @@ void osedit_color(OSEdit *edit, const color_t color) void osedit_bgcolor(OSEdit *edit, const color_t color) { - cassert_no_null(edit); - if (edit->tview != NULL) - { - cassert(edit_get_type(edit->flags) == ekEDIT_MULTI); - if (edit->bgcolor != NULL) - { - g_object_unref(edit->bgcolor); - edit->bgcolor = NULL; - } - - if (color != kCOLOR_TRANSPARENT) - { - char_t rgb[64]; - String *css = NULL; - _oscontrol_to_css_rgb(color, rgb, sizeof(rgb)); - css = str_printf("%s {background:%s;} %s:selected {background-color:@selected_bg_color;}", osglobals_css_entry(), rgb, osglobals_css_entry()); - edit->bgcolor = gtk_css_provider_new(); - gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(edit->bgcolor), (gchar *)tc(css), -1, NULL); - str_destroy(&css); - } - } - else - { - cassert(edit_get_type(edit->flags) == ekEDIT_SINGLE); - if (edit->bgcolor != NULL) - { - _oscontrol_widget_remove_provider(edit->control.widget, edit->bgcolor); - edit->bgcolor = NULL; - } - - if (color != kCOLOR_TRANSPARENT) - { - char_t rgb[64]; - String *css = NULL; - _oscontrol_to_css_rgb(color, rgb, sizeof(rgb)); - css = str_printf("%s {background:%s;} %s:selected {background-color:@selected_bg_color;}", osglobals_css_entry(), rgb, osglobals_css_entry()); - _oscontrol_widget_set_provider(edit->control.widget, tc(css), &edit->bgcolor); - str_destroy(&css); - } - } + const char_t *cssobj = NULL; + GtkWidget *widget = NULL; + cssobj = osglobals_css_entry(); + i_cssobjs(edit, NULL, &widget); + _oscontrol_update_css_bgcolor(widget, cssobj, color, &edit->css_bgcolor); } /*---------------------------------------------------------------------------*/ void osedit_vpadding(OSEdit *edit, const real32_t padding) { - const char_t *entry = osglobals_css_entry(); - uint32_t mpad = (uint32_t)((padding / 2) + .5f); - char_t css[256]; - cassert_no_null(edit); -#if GTK_CHECK_VERSION(3, 22, 0) - bstd_sprintf(css, sizeof(css), "%s {padding-top:%dpx;padding-bottom:%dpx;min-height:0px}", entry, mpad, mpad); -#else - bstd_sprintf(css, sizeof(css), "%s {padding-top:%dpx;padding-bottom:%dpx}", entry, mpad, mpad); -#endif - _oscontrol_widget_set_css(edit->control.widget, css); + const char_t *cssobj = NULL; + GtkWidget *widget = NULL; + edit->vpadding = padding >= 0 ? (uint32_t)padding : kENTRY_VPADDING; + i_cssobjs(edit, &cssobj, &widget); + _oscontrol_update_css_padding(widget, cssobj, edit->vpadding, edit->hpadding, &edit->css_padding); } /*---------------------------------------------------------------------------*/ @@ -710,7 +686,7 @@ void osedit_bounds(const OSEdit *edit, const real32_t refwidth, const uint32_t l } *width = refwidth; - *height = (real32_t)s.height + edit->fsize - edit->fsize_render; + *height = (real32_t)s.height + font_size(edit->font) - edit->fsize_render; } /*---------------------------------------------------------------------------*/ diff --git a/src/osgui/gtk/osglobals.c b/src/osgui/gtk/osglobals.c index 6071b260..87ee8f84 100644 --- a/src/osgui/gtk/osglobals.c +++ b/src/osgui/gtk/osglobals.c @@ -12,8 +12,10 @@ #include "osglobals.h" #include "osglobals_gtk.inl" +#include "osgui.inl" #include "oscontrol_gtk.inl" #include +#include #include #include #include @@ -61,12 +63,23 @@ static color_t kHOTTX_COLOR = 0; static color_t kTEXTBACKDROP_COLOR = 0; static color_t kSELTXBACKDROP_COLOR = 0; static color_t kHOTTXBACKDROP_COLOR = 0; +static String *kCSS_LABEL = NULL; static String *kCSS_ENTRY = NULL; static String *kCSS_BUTTON = NULL; +static String *kCSS_RADIO = NULL; +static String *kCSS_CHECK = NULL; static String *kCSS_COMBOBOX = NULL; static String *kCSS_FRAME = NULL; static String *kCSS_TEXTVIEW = NULL; static String *kCSS_TEXTVIEWTEXT = NULL; +const uint32_t kBUTTON_VPADDING = 8; +const uint32_t kBUTTON_HPADDING = 16; +const uint32_t kPOPUP_VPADDING = 8; +const uint32_t kPOPUP_HPADDING = 16; +const uint32_t kENTRY_VPADDING = 8; +const uint32_t kENTRY_HPADDING = 8; +const uint32_t kBUTTON_IMAGE_SEP = 4; +const uint32_t kCHECKBOX_IMAGE_SEP = 8; /*---------------------------------------------------------------------------*/ @@ -385,14 +398,15 @@ static gboolean i_OnWindowDamage(GtkWidget *widget, GdkEventExpose *event, gpoin i_IMPOSTOR_MAPPED = TRUE; { + Font *font = osgui_create_default_font(); real32_t width, height; cassert(kENTRY_HEIGHT == 0); cassert(kPROGRESS_HEIGHT == 0); - _oscontrol_widget_size(kENTRY, &width, &height); - kENTRY_HEIGHT = (uint32_t)height; - _oscontrol_widget_size(kPROGRESSBAR, &width, &height); + font_extents(font, "OO", -1, &width, &height); + kENTRY_HEIGHT = (uint32_t)height + kENTRY_VPADDING; kPROGRESS_HEIGHT = (uint32_t)height; unref(width); + font_destroy(&font); } i_precompute_colors(); @@ -887,12 +901,16 @@ static void i_parse_gtk_theme(void) str_copy_cn(csect, sizeof(csect), section, nsection); csect[nsection] = '\0'; + if (kCSS_LABEL == NULL) + { + if (i_section(csect, "label", §_n) == TRUE) + kCSS_LABEL = str_cn(csect, sect_n); + } + if (kCSS_ENTRY == NULL) { if (i_section(csect, "entry", §_n) == TRUE) - { kCSS_ENTRY = str_cn(csect, sect_n); - } } if (kCSS_BUTTON == NULL) @@ -901,6 +919,18 @@ static void i_parse_gtk_theme(void) kCSS_BUTTON = str_cn(csect, sect_n); } + if (kCSS_RADIO == NULL) + { + if (i_section(csect, "radiobutton", §_n) == TRUE) + kCSS_RADIO = str_cn(csect, sect_n); + } + + if (kCSS_CHECK == NULL) + { + if (i_section(csect, "checkbutton", §_n) == TRUE) + kCSS_CHECK = str_cn(csect, sect_n); + } + if (kCSS_COMBOBOX == NULL) { if (i_section(csect, "combobox", §_n) == TRUE) @@ -929,7 +959,7 @@ static void i_parse_gtk_theme(void) kCSS_TEXTVIEWTEXT = str_cn(csect, sect_n); } - if (kCSS_ENTRY == NULL || kCSS_BUTTON == NULL || kCSS_COMBOBOX == NULL || kCSS_FRAME == NULL || kCSS_TEXTVIEW == NULL || kCSS_TEXTVIEWTEXT == NULL) + if (kCSS_LABEL == NULL || kCSS_ENTRY == NULL || kCSS_BUTTON == NULL || kCSS_RADIO == NULL || kCSS_CHECK == NULL || kCSS_COMBOBOX == NULL || kCSS_FRAME == NULL || kCSS_TEXTVIEW == NULL || kCSS_TEXTVIEWTEXT == NULL) i_jump_next_section(&pcss); else break; @@ -970,12 +1000,21 @@ static void i_parse_gtk_theme(void) if (kCSS_TEXTVIEWTEXT == NULL && kCSS_TEXTVIEW != NULL) kCSS_TEXTVIEWTEXT = str_copy(kCSS_TEXTVIEW); + if (kCSS_LABEL == NULL) + log_printf("No kCSS_LABEL found in css theme"); + if (kCSS_ENTRY == NULL) log_printf("No kCSS_ENTRY found in css theme"); if (kCSS_BUTTON == NULL) log_printf("No kCSS_BUTTON found in css theme"); + if (kCSS_RADIO == NULL) + log_printf("No kCSS_RADIO found in css theme"); + + if (kCSS_CHECK == NULL) + log_printf("No kCSS_CHECK found in css theme"); + if (kCSS_COMBOBOX == NULL) log_printf("No kCSS_COMBOBOX found in css theme"); @@ -989,6 +1028,8 @@ static void i_parse_gtk_theme(void) /*---------------------------------------------------------------------------*/ +#if !defined(__ASSERTS__) + #if GLIB_CHECK_VERSION(2, 50, 0) static GLogWriterOutput i_null_writter(GLogLevelFlags log_level, const GLogField *fields, gsize n_fields, @@ -1013,18 +1054,22 @@ static void i_null_writter(const gchar *log_domain, GLogLevelFlags log_level, co unref(user_data); } +#endif #endif /*---------------------------------------------------------------------------*/ void osglobals_init(void) { +#if !defined(__ASSERTS__) /* Disable unavoidable GLib/Gtk warnings when processing CSS */ #if GLIB_CHECK_VERSION(2, 50, 0) g_log_set_writer_func(i_null_writter, NULL, NULL); #else g_log_set_default_handler(i_null_writter, NULL); #endif +#endif + i_parse_gtk_theme(); i_impostor_window(); } @@ -1054,8 +1099,11 @@ void osglobals_finish(void) if (kCHECKSBITMAP != NULL) g_object_unref(kCHECKSBITMAP); + str_destopt(&kCSS_LABEL); str_destopt(&kCSS_ENTRY); str_destopt(&kCSS_BUTTON); + str_destopt(&kCSS_RADIO); + str_destopt(&kCSS_CHECK); str_destopt(&kCSS_COMBOBOX); str_destopt(&kCSS_FRAME); str_destopt(&kCSS_TEXTVIEW); @@ -1064,36 +1112,6 @@ void osglobals_finish(void) /*---------------------------------------------------------------------------*/ -static ___INLINE GtkWidget *i_entry(void) -{ - cassert(kENTRY != NULL); - return kENTRY; -} - -/*---------------------------------------------------------------------------*/ - -void osglobals_register_entry(GtkBorder *padding) -{ - if (padding != NULL) - { - GtkWidget *entry = i_entry(); - GtkStyleContext *c = gtk_widget_get_style_context(entry); - gtk_style_context_get_padding(c, GTK_STATE_FLAG_NORMAL, padding); - - if (padding->top == 0 || padding->bottom == 0) - { - GtkRequisition s; - uint32_t fsize; - gtk_widget_get_preferred_size(entry, &s, NULL); - fsize = _oscontrol_widget_font_size(entry); - padding->top = (s.height - fsize - 2) / 2; - padding->bottom = padding->top; - } - } -} - -/*---------------------------------------------------------------------------*/ - bool_t osglobals_impostor_mapped(void) { return i_IMPOSTOR_MAPPED; @@ -1125,6 +1143,14 @@ GtkStyleContext *osglobals_table_context(void) /*---------------------------------------------------------------------------*/ +const char_t *osglobals_css_label(void) +{ + cassert(str_empty(kCSS_LABEL) == FALSE); + return tc(kCSS_LABEL); +} + +/*---------------------------------------------------------------------------*/ + const char_t *osglobals_css_entry(void) { cassert(str_empty(kCSS_ENTRY) == FALSE); @@ -1141,6 +1167,22 @@ const char_t *osglobals_css_button(void) /*---------------------------------------------------------------------------*/ +const char_t *osglobals_css_radio(void) +{ + cassert(str_empty(kCSS_RADIO) == FALSE); + return tc(kCSS_RADIO); +} + +/*---------------------------------------------------------------------------*/ + +const char_t *osglobals_css_check(void) +{ + cassert(str_empty(kCSS_CHECK) == FALSE); + return tc(kCSS_CHECK); +} + +/*---------------------------------------------------------------------------*/ + const char_t *osglobals_css_combobox(void) { cassert(str_empty(kCSS_COMBOBOX) == FALSE); diff --git a/src/osgui/gtk/osglobals_gtk.inl b/src/osgui/gtk/osglobals_gtk.inl index 6f2cac9d..96bcc7d2 100644 --- a/src/osgui/gtk/osglobals_gtk.inl +++ b/src/osgui/gtk/osglobals_gtk.inl @@ -18,8 +18,6 @@ void osglobals_init(void); void osglobals_finish(void); -void osglobals_register_entry(GtkBorder *padding); - bool_t osglobals_impostor_mapped(void); GtkStyleContext *osglobals_entry_context(void); @@ -28,10 +26,16 @@ GtkStyleContext *osglobals_button_context(void); GtkStyleContext *osglobals_table_context(void); +const char_t *osglobals_css_label(void); + const char_t *osglobals_css_entry(void); const char_t *osglobals_css_button(void); +const char_t *osglobals_css_radio(void); + +const char_t *osglobals_css_check(void); + const char_t *osglobals_css_combobox(void); const char_t *osglobals_css_textview(void); @@ -68,4 +72,20 @@ String *osglobals_frame_focus_css(void); void osglobals_restore_focus(GtkWidget *window, GtkWidget *widget); +extern const uint32_t kBUTTON_VPADDING; + +extern const uint32_t kBUTTON_HPADDING; + +extern const uint32_t kPOPUP_VPADDING; + +extern const uint32_t kPOPUP_HPADDING; + +extern const uint32_t kENTRY_VPADDING; + +extern const uint32_t kENTRY_HPADDING; + +extern const uint32_t kBUTTON_IMAGE_SEP; + +extern const uint32_t kCHECKBOX_IMAGE_SEP; + __END_C diff --git a/src/osgui/gtk/osgui_gtk.c b/src/osgui/gtk/osgui_gtk.c index f34dfb26..7b135dcc 100644 --- a/src/osgui/gtk/osgui_gtk.c +++ b/src/osgui/gtk/osgui_gtk.c @@ -33,9 +33,6 @@ /*---------------------------------------------------------------------------*/ -static PangoContext *kPANGO_CONTEXT = NULL; -PangoLayout *kPANGO_LAYOUT = NULL; -real32_t kPANGO_FROM_PIXELS = 0.f; static GdkCursor *kNS_RESIZE_CURSOR = NULL; static GdkCursor *kEW_RESIZE_CURSOR = NULL; static GdkCursor *kDEFAULT_CURSOR = NULL; @@ -185,9 +182,6 @@ void osgui_finish_imp(void) { osglobals_finish(); - g_object_unref((gpointer)kPANGO_LAYOUT); - g_object_unref((gpointer)kPANGO_CONTEXT); - if (kNS_RESIZE_CURSOR != NULL) { g_object_unref(kNS_RESIZE_CURSOR); @@ -204,20 +198,6 @@ void osgui_finish_imp(void) /*---------------------------------------------------------------------------*/ -void osgui_word_size(StringSizeData *data, const char_t *word, real32_t *width, real32_t *height) -{ - int w, h; - cassert_no_null(data); - cassert_no_null(width); - cassert_no_null(height); - pango_layout_set_text(data->layout, (const char *)word, -1); - pango_layout_get_pixel_size(data->layout, &w, &h); - *width = (real32_t)w; - *height = (real32_t)h; -} - -/*---------------------------------------------------------------------------*/ - void osgui_attach_menubar(OSWindow *window, OSMenu *menu) { _osmenu_menubar(menu, window); @@ -258,6 +238,11 @@ const char_t *_osgui_register_icon(const Image *image) kREGISTER_ICONS = arrpt_create(Image); bstd_sprintf(ICON_NAME, 32, "%p", (void *)image); + /* + * Avoid to register the same image twice + * gtk_icon_theme does not provide functions to 'remove' icon once added. + * All registered icons are persisten for all application life-cycle. + */ if (arrpt_find(kREGISTER_ICONS, image, Image) == UINT32_MAX) { uint32_t width = image_width(image); @@ -265,6 +250,7 @@ const char_t *_osgui_register_icon(const Image *image) #pragma GCC diagnostic ignored "-Wdeprecated-declarations" gtk_icon_theme_add_builtin_icon(ICON_NAME, (gint)width, (GdkPixbuf *)image_native(image)); #pragma GCC diagnostic pop + arrpt_append(kREGISTER_ICONS, image, Image); } return ICON_NAME; @@ -390,6 +376,92 @@ uint32_t _osgui_underline_gtk_text(const char_t *text, char_t *buff, const uint3 /*---------------------------------------------------------------------------*/ +void _osgui_underline_markup(const char_t *text, const uint32_t pos, char_t *buff, const uint32_t size) +{ + uint32_t i = 0, offset = 0; + while (*text != 0 && offset < size) + { + uint32_t nbytes = 0; + uint32_t cp = unicode_to_u32b(text, ekUTF8, &nbytes); + + /* This is the character to be underlined */ + if (i == pos) + { + const char_t *span = ""; + uint32_t len = str_len_c(span); + if (size - offset > len) + { + str_copy_c(buff + offset, size - offset, span); + offset += len; + } + + /* Jump the underscore */ + text += nbytes; + cp = unicode_to_u32b(text, ekUTF8, &nbytes); + } + + /* Copy the character to buffer */ + if (size - offset > nbytes) + { + unicode_to_char(cp, buff + offset, ekUTF8); + offset += nbytes; + } + + /* Close the underlined markup */ + if (i == pos) + { + const char_t *span = ""; + uint32_t len = str_len_c(span); + if (size - offset > len) + { + str_copy_c(buff + offset, size - offset, span); + offset += len; + } + } + + /* Next char */ + i += 1; + text += nbytes; + } + + buff[offset] = 0; +} + +/*---------------------------------------------------------------------------*/ + +void _osgui_underline_plain(const char_t *text, const uint32_t pos, char_t *buff, const uint32_t size) +{ + uint32_t i = 0, offset = 0; + while (*text != 0 && offset < size) + { + uint32_t nbytes = 0; + uint32_t cp = unicode_to_u32b(text, ekUTF8, &nbytes); + + /* This is the character to be underlined */ + if (i == pos) + { + /* Jump the underscore */ + text += nbytes; + cp = unicode_to_u32b(text, ekUTF8, &nbytes); + } + + /* Copy the character to buffer */ + if (size - offset > nbytes) + { + unicode_to_char(cp, buff + offset, ekUTF8); + offset += nbytes; + } + + /* Next char */ + i += 1; + text += nbytes; + } + + buff[offset] = 0; +} + +/*---------------------------------------------------------------------------*/ + vkey_t _osgui_vkey(guint kval) { vkey_t key = ENUM_MAX(vkey_t); @@ -483,11 +555,6 @@ bool_t osgui_is_pre_initialized_imp(void) void osgui_pre_initialize_imp(void) { - PangoFontMap *fontmap = pango_cairo_font_map_get_default(); - real32_t dpi = (real32_t)pango_cairo_font_map_get_resolution((PangoCairoFontMap *)fontmap); - kPANGO_CONTEXT = pango_font_map_create_context(fontmap); - kPANGO_LAYOUT = pango_layout_new(kPANGO_CONTEXT); - kPANGO_FROM_PIXELS = 1.f / (dpi / 72.f) * (real32_t)PANGO_SCALE; kREGISTER_ICONS = NULL; /* Set the default font */ diff --git a/src/osgui/gtk/osgui_gtk.inl b/src/osgui/gtk/osgui_gtk.inl index c0169bfd..f2c347da 100644 --- a/src/osgui/gtk/osgui_gtk.inl +++ b/src/osgui/gtk/osgui_gtk.inl @@ -14,10 +14,6 @@ __EXTERN_C -extern PangoLayout *kPANGO_LAYOUT; - -extern real32_t kPANGO_FROM_PIXELS; - const char_t *_osgui_register_icon(const Image *image); void _osgui_register_entry(GtkBorder *padding); @@ -30,6 +26,10 @@ void _osgui_default_cursor(GtkWidget *widget); uint32_t _osgui_underline_gtk_text(const char_t *text, char_t *buff, const uint32_t size); +void _osgui_underline_markup(const char_t *text, const uint32_t pos, char_t *buff, const uint32_t size); + +void _osgui_underline_plain(const char_t *text, const uint32_t pos, char_t *buff, const uint32_t size); + vkey_t _osgui_vkey(const guint keyval); uint32_t _osgui_modifiers(const guint state); diff --git a/src/osgui/gtk/oslabel.c b/src/osgui/gtk/oslabel.c index 03906621..095a4c7b 100644 --- a/src/osgui/gtk/oslabel.c +++ b/src/osgui/gtk/oslabel.c @@ -15,6 +15,7 @@ #include "osgui.inl" #include "oslistener.inl" #include "osgui_gtk.inl" +#include "osglobals_gtk.inl" #include "oscontrol_gtk.inl" #include "ospanel_gtk.inl" #include @@ -22,6 +23,7 @@ #include #include #include +#include #include #if !defined(__GTK3__) @@ -34,11 +36,20 @@ struct _oslabel_t GtkWidget *label; String *text; Font *font; - color_t _color; - GtkCssProvider *bgcolor; + uint32_t flags; + color_t tcolor; + color_t bgcolor; + align_t align; + PangoEllipsizeMode ellipsis; + bool_t layout_updated; + PangoLayout *layout; gint enter_signal; gint exit_signal; gint click_signal; + real32_t control_width; + real32_t control_height; + real32_t text_width; + real32_t text_height; Listener *OnClick; Listener *OnMouseEnter; Listener *OnMouseExit; @@ -100,36 +111,14 @@ static gboolean i_OnClick(GtkWidget *widget, GdkEventButton *event, OSLabel *lab /*---------------------------------------------------------------------------*/ -static uint32_t i_font_px(const real32_t fsize, const uint32_t fstyle) -{ - if ((fstyle & ekFPOINTS) == ekFPOINTS) - { - PangoFontMap *fontmap = pango_cairo_font_map_get_default(); - real32_t dpi = (real32_t)pango_cairo_font_map_get_resolution((PangoCairoFontMap *)fontmap); - return (uint32_t)(fsize / (dpi / 72.f)); - } - else - { - return (uint32_t)fsize; - } -} - -/*---------------------------------------------------------------------------*/ - static void i_set_text(OSLabel *label) { - PangoFontDescription *fdesc = (PangoFontDescription *)font_native(label->font); - const char *family = pango_font_description_get_family(fdesc); - real32_t fsize = font_size(label->font); - uint32_t fstyle = font_style(label->font); - real32_t fpoints = i_font_px(fsize, fstyle); - String *format = str_printf("font); - if (fstyle & ekFBOLD) - str_cat(&format, " weight=\"bold\""); + format = str_printf("_color != 0) { char_t html[16]; - color_to_html(label->_color, html, sizeof(html)); + color_to_html(label->tcolor != kCOLOR_DEFAULT ? label->tcolor : ekSYSCOLOR_LABEL, html, sizeof(html)); str_cat(&format, " foreground=\""); str_cat(&format, html); str_cat(&format, "\""); @@ -150,38 +138,67 @@ static void i_set_text(OSLabel *label) str_cat(&format, tc(label->text)); str_cat(&format, ""); - gtk_label_set_markup(GTK_LABEL(label->label), tc(format)); + pango_layout_set_markup(label->layout, tc(format), -1); str_destroy(&format); } /*---------------------------------------------------------------------------*/ -static void i_set_bg_color(OSLabel *label, const color_t color) +static gboolean i_OnDraw(GtkWidget *widget, cairo_t *cr, OSLabel *label) { + real32_t xscale = 1; cassert_no_null(label); + cassert_unref(widget == label->label, widget); + + xscale = font_xscale(label->font); + + if (label->layout == NULL) + label->layout = pango_cairo_create_layout(cr); + + if (label->layout_updated == FALSE) + { + const PangoFontDescription *fdesc = cast(font_native(label->font), PangoFontDescription); + pango_layout_set_font_description(label->layout, fdesc); + pango_layout_set_width(label->layout, (int)((label->control_width / xscale) * PANGO_SCALE)); + pango_layout_set_height(label->layout, -1); + pango_layout_set_ellipsize(label->layout, label->ellipsis); + i_set_text(label); + label->layout_updated = TRUE; + } -#if GTK_CHECK_VERSION(3, 22, 0) - if (label->bgcolor != NULL) + if (label->bgcolor != kCOLOR_DEFAULT) { - _oscontrol_widget_remove_provider(label->label, label->bgcolor); - label->bgcolor = NULL; + real32_t r, g, b, a; + color_get_rgbaf(label->bgcolor, &r, &g, &b, &a); + cairo_set_source_rgba(cr, (double)r, (double)g, (double)b, (double)a); + cairo_rectangle(cr, 0, 0, (double)label->control_width, (double)label->control_height); + cairo_fill(cr); } - if (color != kCOLOR_TRANSPARENT) - _oscontrol_widget_bg_color(label->label, "label", color, &label->bgcolor); + cairo_save(cr); -#else + if (label->control_width > label->text_width) { - GdkRGBA gdkcolor; - _oscontrol_to_gdkrgba(color, &gdkcolor); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - gtk_widget_override_background_color(label->control.widget, GTK_STATE_FLAG_NORMAL, &gdkcolor); - gtk_widget_override_background_color(label->label, GTK_STATE_FLAG_NORMAL, &gdkcolor); -#pragma GCC diagnostic pop + switch (label->align) + { + case ekLEFT: + case ekJUSTIFY: + break; + case ekCENTER: + cairo_translate(cr, (double)((label->control_width - label->text_width) / 2), 0); + break; + case ekRIGHT: + cairo_translate(cr, (double)(label->control_width - label->text_width), 0); + break; + } } -#endif + cairo_scale(cr, xscale, 1); + pango_cairo_show_layout(cr, label->layout); + cairo_restore(cr); + + /* Stop other handlers from being invoked for the event */ + return TRUE; } /*---------------------------------------------------------------------------*/ @@ -191,16 +208,18 @@ OSLabel *oslabel_create(const uint32_t flags) OSLabel *label = heap_new0(OSLabel); GtkWidget *widget = gtk_event_box_new(); _oscontrol_init(&label->control, ekGUI_TYPE_LABEL, widget, widget, TRUE); - label->label = gtk_label_new(NULL); + label->label = gtk_drawing_area_new(); label->text = str_c(""); label->font = osgui_create_default_font(); - label->_color = kCOLOR_DEFAULT; - gtk_label_set_use_markup(GTK_LABEL(label->label), TRUE); + label->flags = flags; + label->tcolor = kCOLOR_DEFAULT; + label->bgcolor = kCOLOR_DEFAULT; + label->align = ekLEFT; + label->ellipsis = PANGO_ELLIPSIZE_NONE; + label->layout_updated = FALSE; + g_signal_connect(G_OBJECT(label->label), "draw", G_CALLBACK(i_OnDraw), label); gtk_widget_show(label->label); gtk_container_add(GTK_CONTAINER(widget), label->label); - gtk_label_set_line_wrap(GTK_LABEL(label->label), label_get_type(flags) == ekLABEL_MULTI ? TRUE : FALSE); - i_set_text(label); - i_set_bg_color(label, kCOLOR_TRANSPARENT); return label; } @@ -210,19 +229,18 @@ void oslabel_destroy(OSLabel **label) { cassert_no_null(label); cassert_no_null(*label); - - if ((*label)->bgcolor != NULL) - { - /* Not g_object_unref - g_object_unref((*label)->bgcolor); */ - (*label)->bgcolor = NULL; - } - listener_destroy(&(*label)->OnClick); listener_destroy(&(*label)->OnMouseEnter); listener_destroy(&(*label)->OnMouseExit); font_destroy(&(*label)->font); str_destroy(&(*label)->text); + + if ((*label)->layout != NULL) + { + g_object_unref((*label)->layout); + (*label)->layout = NULL; + } + _oscontrol_destroy(*(OSControl **)label); heap_delete(label, OSLabel); } @@ -260,7 +278,8 @@ void oslabel_text(OSLabel *label, const char_t *text) { cassert_no_null(label); str_upd(&label->text, text); - i_set_text(label); + label->layout_updated = FALSE; + gtk_widget_queue_draw(label->label); } /*---------------------------------------------------------------------------*/ @@ -268,53 +287,23 @@ void oslabel_text(OSLabel *label, const char_t *text) void oslabel_font(OSLabel *label, const Font *font) { cassert_no_null(label); - if (font != label->font) + if (font_equals(font, label->font) == FALSE) { font_destroy(&label->font); label->font = font_copy(font); - i_set_text(label); + label->layout_updated = FALSE; + gtk_widget_queue_draw(label->label); } } /*---------------------------------------------------------------------------*/ -static ___INLINE GtkJustification i_align(const align_t align, gfloat *xalign) -{ - switch (align) - { - case ekLEFT: - *xalign = 0; - return GTK_JUSTIFY_LEFT; - case ekJUSTIFY: - *xalign = 0; - return GTK_JUSTIFY_FILL; - case ekCENTER: - *xalign = .5f; - return GTK_JUSTIFY_CENTER; - case ekRIGHT: - *xalign = .99f; - return GTK_JUSTIFY_RIGHT; - cassert_default(); - } - - return GTK_JUSTIFY_LEFT; -} - -/*---------------------------------------------------------------------------*/ - void oslabel_align(OSLabel *label, const align_t align) { - gfloat xalign = 0; cassert_no_null(label); - gtk_label_set_justify(GTK_LABEL(label->label), i_align(align, &xalign)); - -#if GTK_CHECK_VERSION(3, 16, 0) - gtk_label_set_xalign(GTK_LABEL(label->label), xalign); - -#else - gtk_misc_set_alignment(GTK_MISC(label->label), xalign, 0); - -#endif + label->align = align; + label->layout_updated = TRUE; + gtk_widget_queue_draw(label->label); } /*---------------------------------------------------------------------------*/ @@ -342,9 +331,10 @@ static PangoEllipsizeMode i_ellipsis(const ellipsis_t ellipsis) void oslabel_ellipsis(OSLabel *label, const ellipsis_t ellipsis) { - PangoEllipsizeMode e = i_ellipsis(ellipsis); cassert_no_null(label); - gtk_label_set_ellipsize(GTK_LABEL(label->label), e); + label->ellipsis = i_ellipsis(ellipsis); + label->layout_updated = FALSE; + gtk_widget_queue_draw(label->label); } /*---------------------------------------------------------------------------*/ @@ -352,10 +342,10 @@ void oslabel_ellipsis(OSLabel *label, const ellipsis_t ellipsis) void oslabel_color(OSLabel *label, const color_t color) { cassert_no_null(label); - if (label->_color != color) + if (label->tcolor != color) { - label->_color = color; - i_set_text(label); + label->tcolor = color; + gtk_widget_queue_draw(label->label); } } @@ -363,40 +353,25 @@ void oslabel_color(OSLabel *label, const color_t color) void oslabel_bgcolor(OSLabel *label, const color_t color) { - i_set_bg_color(label, color); + cassert_no_null(label); + if (label->bgcolor != color) + { + label->bgcolor = color; + gtk_widget_queue_draw(label->label); + } } /*---------------------------------------------------------------------------*/ void oslabel_bounds(const OSLabel *label, const char_t *text, const real32_t refwidth, real32_t *width, real32_t *height) { - PangoLayout *layout; - int w, h; - String *curstr = NULL; cassert_no_null(label); cassert_no_null(width); cassert_no_null(height); - - if (str_equ(label->text, text) == FALSE) - { - curstr = str_copy(label->text); - str_upd(&((OSLabel *)label)->text, text); - i_set_text((OSLabel *)label); - } - - layout = gtk_label_get_layout(GTK_LABEL(label->label)); - pango_layout_set_width(layout, refwidth > 0.f ? (int)(refwidth * (real32_t)PANGO_SCALE) : -1); - pango_layout_get_pixel_size(layout, &w, &h); - - if (curstr != NULL) - { - str_upd(&((OSLabel *)label)->text, tc(curstr)); - i_set_text((OSLabel *)label); - str_destroy(&curstr); - } - - *width = (real32_t)w; - *height = (real32_t)h; + font_extents(label->font, text, refwidth, width, height); + cast(label, OSLabel)->text_width = *width; + cast(label, OSLabel)->text_height = *height; + cast(label, OSLabel)->layout_updated = FALSE; } /*---------------------------------------------------------------------------*/ @@ -431,7 +406,11 @@ void oslabel_enabled(OSLabel *label, const bool_t enabled) void oslabel_size(const OSLabel *label, real32_t *width, real32_t *height) { - _oscontrol_get_size((const OSControl *)label, width, height); + cassert_no_null(label); + cassert_no_null(width); + cassert_no_null(height); + *width = label->control_width; + *height = label->control_height; } /*---------------------------------------------------------------------------*/ @@ -445,12 +424,11 @@ void oslabel_origin(const OSLabel *label, real32_t *x, real32_t *y) void oslabel_frame(OSLabel *label, const real32_t x, const real32_t y, const real32_t width, const real32_t height) { - _oscontrol_set_frame((OSControl *)label, x, y, width, height); - -#if GTK_CHECK_VERSION(3, 10, 0) - /* Internal label doesn't need resize */ -#else cassert_no_null(label); + _oscontrol_set_frame((OSControl *)label, x, y, width, height); gtk_widget_set_size_request(label->label, (gint)width, (gint)height); -#endif + label->control_width = width; + label->control_height = height; + label->layout_updated = FALSE; + gtk_widget_queue_draw(label->label); } diff --git a/src/osgui/gtk/osmenuitem.c b/src/osgui/gtk/osmenuitem.c index a3be955e..bf3403a8 100644 --- a/src/osgui/gtk/osmenuitem.c +++ b/src/osgui/gtk/osmenuitem.c @@ -195,7 +195,7 @@ OSMenuItem *osmenuitem_create(const uint32_t flags) item->label = gtk_accel_label_new(""); item->key = ENUM_MAX(vkey_t); item->launch_event = TRUE; - _oscontrol_widget_set_css(item->box, "box {padding-top:1px;padding-bottom:1px;}"); + /*_oscontrol_widget_set_css(item->box, "box {padding-top:1px;padding-bottom:1px;}");*/ gtk_label_set_use_underline(GTK_LABEL(item->label), TRUE); gtk_accel_label_set_accel_widget(GTK_ACCEL_LABEL(item->label), item->widget); #if GTK_CHECK_VERSION(3, 16, 0) diff --git a/src/osgui/gtk/ospopup.c b/src/osgui/gtk/ospopup.c index bd3146a3..b20f7903 100644 --- a/src/osgui/gtk/ospopup.c +++ b/src/osgui/gtk/ospopup.c @@ -40,7 +40,9 @@ struct _ospopup_t GtkWidget *button; GtkCellRenderer *imgcell; GtkCellRenderer *txtcell; - GtkCssProvider *font; + Font *font; + GtkCssProvider *css_padding; + GtkCssProvider *css_font; bool_t launch_event; Listener *OnSelect; ArrPt(String) *texts; @@ -108,7 +110,7 @@ OSPopUp *ospopup_create(const uint32_t flags) OSPopUp *popup = heap_new0(OSPopUp); GtkWidget *widget = gtk_combo_box_new(); Font *font = osgui_create_default_font(); - const char_t *csscombo = osglobals_css_combobox(); + const char_t *cssobj = osglobals_css_combobox(); cassert_unref(flags == ekPOPUP_FLAG, flags); #if GTK_CHECK_VERSION(3, 16, 0) @@ -148,7 +150,8 @@ OSPopUp *ospopup_create(const uint32_t flags) gtk_widget_show(popup->popup); _oscontrol_init(&popup->control, ekGUI_TYPE_POPUP, widget, popup->button, TRUE); - _oscontrol_widget_font(popup->popup, csscombo, font, &popup->font); + _oscontrol_update_css_font(popup->popup, cssobj, font, &popup->font, &popup->css_font); + _oscontrol_update_css_padding(popup->button, osglobals_css_button(), kPOPUP_VPADDING, kPOPUP_HPADDING, &popup->css_padding); popup->fsize = (uint32_t)(font_size(font) + 2.5f); font_destroy(&font); popup->launch_event = TRUE; @@ -175,6 +178,9 @@ void ospopup_destroy(OSPopUp **popup) listener_destroy(&(*popup)->OnSelect); arrpt_destroy(&(*popup)->texts, str_destroy, String); arrpt_destroy(&(*popup)->images, i_img_dest, Image); + font_destroy(&(*popup)->font); + _oscontrol_destroy_css_provider(&(*popup)->css_padding); + _oscontrol_destroy_css_provider(&(*popup)->css_font); _oscontrol_destroy(*(OSControl **)popup); heap_delete(popup, OSPopUp); } @@ -225,11 +231,10 @@ void ospopup_tooltip(OSPopUp *popup, const char_t *text) void ospopup_font(OSPopUp *popup, const Font *font) { - const char_t *csscombo = osglobals_css_combobox(); + const char_t *cssobj = osglobals_css_combobox(); cassert_no_null(popup); cassert(GTK_IS_EVENT_BOX(popup->control.widget)); - _oscontrol_widget_remove_provider(popup->popup, popup->font); - _oscontrol_widget_font(popup->popup, csscombo, font, &popup->font); + _oscontrol_update_css_font(popup->popup, cssobj, font, &popup->font, &popup->css_font); popup->fsize = (uint32_t)(font_size(font) + 2.5f); } diff --git a/src/osgui/gtk/ostext.c b/src/osgui/gtk/ostext.c index 69460776..45a46faf 100644 --- a/src/osgui/gtk/ostext.c +++ b/src/osgui/gtk/ostext.c @@ -51,11 +51,11 @@ struct _ostext_t GtkWidget *tview; GtkTextBuffer *buffer; gchar *text_cache; - GtkCssProvider *fontcss; - GtkCssProvider *colorcss; - GtkCssProvider *bgcolorcss; - GtkCssProvider *pgcolorcss; - GtkCssProvider *border_color; + GtkCssProvider *css_font; + GtkCssProvider *css_color; + GtkCssProvider *css_bgcolor; + GtkCssProvider *css_pgcolor; + GtkCssProvider *css_bdcolor; OSControl *capture; Listener *OnFilter; Listener *OnFocus; @@ -221,15 +221,18 @@ OSText *ostext_create(const uint32_t flags) /* Creating the frame (border) view */ { GtkWidget *frame = gtk_frame_new(NULL); - String *css = osglobals_frame_focus_css(); cassert(gtk_widget_get_has_window(frame) == FALSE); gtk_container_add(GTK_CONTAINER(frame), top); gtk_widget_show(top); - view->border_color = gtk_css_provider_new(); - gtk_css_provider_load_from_data(view->border_color, tc(css), -1, NULL); + + { + String *css = osglobals_frame_focus_css(); + view->css_bdcolor = _oscontrol_css_provider(tc(css)); + str_destroy(&css); + } + g_object_set_data(G_OBJECT(scrolled), "OSControl", &view->control); gtk_widget_set_state_flags(frame, GTK_STATE_FLAG_FOCUSED, FALSE); - str_destroy(&css); top = frame; } @@ -267,20 +270,17 @@ void ostext_destroy(OSText **view) listener_destroy(&(*view)->OnFocus); gtk_container_remove(GTK_CONTAINER(i_scrolled_window(*view)), (*view)->tview); - if ((*view)->border_color != NULL) - { - cassert(GTK_IS_FRAME((*view)->control.widget)); - _oscontrol_widget_remove_provider((*view)->control.widget, (*view)->border_color); - g_object_unref((*view)->border_color); - (*view)->border_color = NULL; - } - if ((*view)->text_cache != NULL) { g_free((*view)->text_cache); (*view)->text_cache = NULL; } + _oscontrol_destroy_css_provider(&(*view)->css_font); + _oscontrol_destroy_css_provider(&(*view)->css_color); + _oscontrol_destroy_css_provider(&(*view)->css_bgcolor); + _oscontrol_destroy_css_provider(&(*view)->css_pgcolor); + _oscontrol_destroy_css_provider(&(*view)->css_bdcolor); _oscontrol_destroy(*(OSControl **)view); heap_delete(view, OSText); } @@ -462,33 +462,10 @@ static GtkTextTag *i_tag_attribs(OSText *view) static void i_global_attribs(OSText *view) { - const char_t *csstype = osglobals_css_textview(); - - if (view->fontcss != NULL) - { - _oscontrol_widget_remove_provider(view->tview, view->fontcss); - view->fontcss = NULL; - } - - if (view->colorcss != NULL) - { - _oscontrol_widget_remove_provider(view->tview, view->colorcss); - view->colorcss = NULL; - } - - if (view->bgcolorcss != NULL) - { - _oscontrol_widget_remove_provider(view->tview, view->bgcolorcss); - view->bgcolorcss = NULL; - } - - _oscontrol_widget_font_desc(view->tview, csstype, view->ffamily, view->fsize, view->fstyle, &view->fontcss); - - if (view->color != kCOLOR_DEFAULT) - _oscontrol_widget_color(view->tview, csstype, view->color, &view->colorcss); - - if (view->bgcolor != kCOLOR_DEFAULT) - _oscontrol_widget_bg_color(view->tview, csstype, view->bgcolor, &view->bgcolorcss); + const char_t *cssobj = osglobals_css_textview(); + _oscontrol_update_css_font_desc(view->tview, cssobj, view->ffamily, view->fsize, view->fstyle, &view->css_font); + _oscontrol_update_css_color(view->tview, cssobj, view->color, &view->css_color); + _oscontrol_update_css_bgcolor(view->tview, cssobj, view->bgcolor, &view->css_bgcolor); { GtkJustification justif = _oscontrol_justification(view->align); @@ -749,15 +726,11 @@ void ostext_property(OSText *view, const gui_text_t prop, const void *value) break; case ekGUI_TEXT_PGCOLOR: - if (view->pgcolorcss != NULL) - { - _oscontrol_widget_remove_provider(view->tview, view->pgcolorcss); - view->pgcolorcss = NULL; - } - - if (*(color_t *)value != kCOLOR_DEFAULT) - _oscontrol_widget_bg_color(view->tview, "textview", *(color_t *)value, &view->pgcolorcss); + { + const char_t *cssobj = osglobals_css_textview(); + _oscontrol_update_css_bgcolor(view->tview, cssobj, *(color_t *)value, &view->css_pgcolor); break; + } case ekGUI_TEXT_PARALIGN: if (view->align != *((align_t *)value)) @@ -994,13 +967,13 @@ void ostext_focus(OSText *view, const bool_t focus) listener_event(view->OnFocus, ekGUI_EVENT_FOCUS, view, ¶ms, NULL, OSText, bool_t, void); } - if (view->border_color != NULL) + if (view->css_bdcolor != NULL) { cassert(GTK_IS_FRAME(view->control.widget)); if (focus == TRUE) - _oscontrol_widget_add_provider(view->control.widget, view->border_color); + _oscontrol_add_css_provider(view->control.widget, view->css_bdcolor); else - _oscontrol_widget_remove_provider(view->control.widget, view->border_color); + _oscontrol_remove_css_provider(view->control.widget, view->css_bdcolor); } if (focus == TRUE) diff --git a/src/osgui/gtk/osupdown.c b/src/osgui/gtk/osupdown.c index b4f8d3b2..9b31e98f 100644 --- a/src/osgui/gtk/osupdown.c +++ b/src/osgui/gtk/osupdown.c @@ -39,7 +39,6 @@ struct _osupdown_t { OSControl control; udstate_t state; - GtkBorder padding; Listener *OnClick; }; @@ -79,7 +78,7 @@ static gboolean i_OnDraw(GtkWidget *widget, cairo_t *cr, OSUpDown *updown) gtk_style_context_set_state(ctx, upstate); gtk_render_frame(ctx, cr, 0, 0, w, (h / 2) + 1); gtk_style_context_set_state(ctx, downstate); - gtk_render_frame(ctx, cr, 0, (h / 2), w, (h / 2)); + gtk_render_frame(ctx, cr, 0, (h / 2) + 1, w, (h / 2)); gtk_style_context_restore(ctx); gtk_render_arrow(ctx, cr, 0, ax, ay, aw); gtk_render_arrow(ctx, cr, G_PI, ax, (h / 2) + ay, aw); @@ -183,7 +182,6 @@ OSUpDown *osupdown_create(const uint32_t flags) g_signal_connect(widget, "button-press-event", G_CALLBACK(i_OnPress), (gpointer)updown); g_signal_connect(widget, "button-release-event", G_CALLBACK(i_OnPress), (gpointer)updown); g_signal_connect(widget, "draw", G_CALLBACK(i_OnDraw), (gpointer)updown); - osglobals_register_entry(&updown->padding); return updown; } @@ -249,6 +247,9 @@ void osupdown_size(const OSUpDown *updown, real32_t *width, real32_t *height) uint32_t eheight = osglobals_entry_height(); cassert_no_null(width); cassert_no_null(height); + if (eheight % 2 == 1) + eheight += 1; + *height = (real32_t)eheight; if (eheight > 32) *width = 32; diff --git a/src/osgui/gtk/osview.c b/src/osgui/gtk/osview.c index 271015ec..36e07cb0 100644 --- a/src/osgui/gtk/osview.c +++ b/src/osgui/gtk/osview.c @@ -44,7 +44,7 @@ struct _osview_t real32_t clip_width; real32_t clip_height; ViewListeners listeners; - GtkCssProvider *border_color; + GtkCssProvider *css_bdcolor; bool_t allow_tab; Listener *OnFocus; Listener *OnResignFocus; @@ -354,13 +354,16 @@ OSView *osview_create(const uint32_t flags) if (flags & ekVIEW_BORDER) { GtkWidget *frame = gtk_frame_new(NULL); - String *css = osglobals_frame_focus_css(); cassert(gtk_widget_get_has_window(frame) == FALSE); gtk_container_add(GTK_CONTAINER(frame), top); gtk_widget_show(top); - view->border_color = gtk_css_provider_new(); - gtk_css_provider_load_from_data(view->border_color, tc(css), -1, NULL); - str_destroy(&css); + + { + String *css = osglobals_frame_focus_css(); + view->css_bdcolor = _oscontrol_css_provider(tc(css)); + str_destroy(&css); + } + top = frame; } @@ -399,20 +402,13 @@ void osview_destroy(OSView **view) listener_destroy(&(*view)->OnAcceptFocus); listener_destroy(&(*view)->OnOverlay); - if ((*view)->border_color != NULL) - { - cassert(GTK_IS_FRAME((*view)->control.widget)); - _oscontrol_widget_remove_provider((*view)->control.widget, (*view)->border_color); - g_object_unref((*view)->border_color); - (*view)->border_color = NULL; - } - if ((*view)->ctx != NULL) dctx_destroy(&(*view)->ctx); if ((*view)->scroll != NULL) osscrolls_destroy(&(*view)->scroll); + _oscontrol_destroy_css_provider(&(*view)->css_bdcolor); _oscontrol_destroy(*(OSControl **)view); heap_delete(view, OSView); } @@ -790,13 +786,13 @@ void osview_focus(OSView *view, const bool_t focus) listener_event(view->OnFocus, ekGUI_EVENT_FOCUS, view, ¶ms, NULL, OSView, bool_t, void); } - if (view->border_color != NULL) + if (view->css_bdcolor != NULL) { cassert(GTK_IS_FRAME(view->control.widget)); if (focus == TRUE) - _oscontrol_widget_add_provider(view->control.widget, view->border_color); + _oscontrol_add_css_provider(view->control.widget, view->css_bdcolor); else - _oscontrol_widget_remove_provider(view->control.widget, view->border_color); + _oscontrol_remove_css_provider(view->control.widget, view->css_bdcolor); } } diff --git a/src/osgui/gtk/oswindow.c b/src/osgui/gtk/oswindow.c index d283568c..d6136a69 100644 --- a/src/osgui/gtk/oswindow.c +++ b/src/osgui/gtk/oswindow.c @@ -45,7 +45,8 @@ struct _oswindow_t OSPanel *main_panel; gint signal_delete; gint signal_config; - gint signal_key; + gint signal_keypre; + gint signal_keyrel; gint signal_state; Listener *OnMoved; Listener *OnResize; @@ -263,6 +264,21 @@ static gboolean i_OnKeyPress(GtkWidget *widget, GdkEventKey *event, OSWindow *wi /*---------------------------------------------------------------------------*/ +static gboolean i_OnKeyRelease(GtkWidget *widget, GdkEventKey *event, OSWindow *window) +{ + guint key = 0; + cassert_no_null(event); + unref(widget); + unref(window); + key = event->keyval; + if (key == GDK_KEY_Alt_L || key == GDK_KEY_Alt_R) + return TRUE; + else + return FALSE; +} + +/*---------------------------------------------------------------------------*/ + static ___INLINE GtkWidget *i_gtk_window(const uint32_t flags) { GtkWidget *window = NULL; @@ -283,9 +299,8 @@ static ___INLINE GtkWidget *i_gtk_window(const uint32_t flags) static gboolean i_OnWindowState(GtkWindow *widget, GdkEventWindowState *event, OSWindow *window) { - unref(widget); cassert_no_null(event); - cassert_no_null(window); + cassert_no_null(widget); if (event->new_window_state & GDK_WINDOW_STATE_FOCUSED) ostabstop_restore(&window->tabstop); return FALSE; @@ -331,7 +346,8 @@ OSWindow *oswindow_create(const uint32_t flags) window->signal_delete = g_signal_connect(G_OBJECT(widget), "delete-event", G_CALLBACK(i_OnClose), (gpointer)window); window->signal_config = g_signal_connect(G_OBJECT(widget), "configure-event", G_CALLBACK(i_OnConfigure), (gpointer)window); - window->signal_key = g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(i_OnKeyPress), (gpointer)window); + window->signal_keypre = g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(i_OnKeyPress), (gpointer)window); + window->signal_keyrel = g_signal_connect(G_OBJECT(widget), "key-release-event", G_CALLBACK(i_OnKeyRelease), (gpointer)window); window->signal_state = g_signal_connect(G_OBJECT(widget), "window-state-event", G_CALLBACK(i_OnWindowState), (gpointer)window); if (i_APP_ICON != NULL) @@ -383,7 +399,8 @@ void oswindow_destroy(OSWindow **window) gtk_widget_hide((*window)->control.widget); g_signal_handler_disconnect(G_OBJECT((*window)->control.widget), (*window)->signal_delete); g_signal_handler_disconnect(G_OBJECT((*window)->control.widget), (*window)->signal_config); - g_signal_handler_disconnect(G_OBJECT((*window)->control.widget), (*window)->signal_key); + g_signal_handler_disconnect(G_OBJECT((*window)->control.widget), (*window)->signal_keypre); + g_signal_handler_disconnect(G_OBJECT((*window)->control.widget), (*window)->signal_keyrel); g_signal_handler_disconnect(G_OBJECT((*window)->control.widget), (*window)->signal_state); if ((*window)->destroy_main_view == TRUE && (*window)->main_panel != NULL) diff --git a/src/osgui/osgui.c b/src/osgui/osgui.c index 2b4f0b23..5d12bf18 100644 --- a/src/osgui/osgui.c +++ b/src/osgui/osgui.c @@ -194,225 +194,6 @@ Font *osgui_create_default_font(void) /*---------------------------------------------------------------------------*/ -static const char_t *i_jump_blanks(const char_t *str) -{ - cassert_no_null(str); - for (; *str != '\0';) - { - if (*str == ' ' || *str == '\t' || *str == '\r') - { - str += 1; - } - else - { - return str; - } - } - - return str; -} - -/*---------------------------------------------------------------------------*/ - -static const char_t *i_jump_not_blanks(const char_t *str) -{ - cassert_no_null(str); - for (; *str != '\0';) - { - if (*str != ' ' && *str != '\t' && *str != '\r' && *str != '\0' && *str != '\n') - { - str += 1; - } - else - { - return str; - } - } - - return str; -} - -/*---------------------------------------------------------------------------*/ -#define i_WORD_TYPE_END 0 -#define i_WORD_TYPE_NEW_LINE 1 -#define i_WORD_TYPE_BLANCKS 2 -#define i_WORD_TYPE_TEXT 3 - -static const char_t *i_next_word(const char_t *str, int *word_type) -{ - cassert_no_null(str); - cassert_no_null(word_type); - { - if (*str == '\0') - { - *word_type = i_WORD_TYPE_END; - return NULL; - } - else if (*str == '\n') - { - *word_type = i_WORD_TYPE_NEW_LINE; - return str + 1; - } - } - - { - const char_t *end = i_jump_blanks(str); - if (end != str) - { - *word_type = i_WORD_TYPE_BLANCKS; - return end; - } - } - - { - const char_t *end = i_jump_not_blanks(str); - cassert(end != str); - *word_type = i_WORD_TYPE_TEXT; - return end; - } -} - -/*---------------------------------------------------------------------------*/ - -static void i_new_line(StringSizeData *data, real32_t *current_width, real32_t *current_height, real32_t *current_width_without_spaces, uint32_t *num_lines, real32_t *width, real32_t *height) -{ - cassert_no_null(current_width); - cassert_no_null(current_height); - cassert_no_null(current_width_without_spaces); - cassert_no_null(num_lines); - cassert_no_null(width); - cassert_no_null(height); - - if (*current_width_without_spaces == 0.f) - { - real32_t word_width = 0.f, word_height = 0.f; - osgui_word_size(data, "A", &word_width, &word_height); - *current_width_without_spaces = 0; - *current_height = word_height; - } - else - { - cassert(*current_height > 0.f); - } - - if (*current_width_without_spaces > *width) - *width = *current_width_without_spaces; - *height += *current_height; - - *current_width = 0.f; - *current_width_without_spaces = 0.f; - *current_height = 0.f; - *num_lines += 1; -} - -/*---------------------------------------------------------------------------*/ - -static real32_t i_ceil(const real32_t n) -{ - int32_t in = (int32_t)n; - if (n == (real32_t)in) - { - return (real32_t)in; - } - - return (real32_t)(in + 1); -} - -/*---------------------------------------------------------------------------*/ - -void osgui_text_bounds(StringSizeData *data, const char_t *text, const real32_t refwidth, real32_t *width, real32_t *height) -{ - uint32_t num_lines = 0; - real32_t ref_width = refwidth > 0.f ? refwidth : 1e8f; - real32_t current_width = 0.f, current_height = 0.f, current_width_without_spaces = 0.f; - const char_t *ctext = text; - cassert_no_null(width); - cassert_no_null(height); - *width = 0.f; - *height = 0.f; - - while (ctext != NULL) - { - const char_t *next_text = NULL; - int word_type = 0; - - next_text = i_next_word(ctext, &word_type); - - switch (word_type) - { - - case i_WORD_TYPE_END: - if (current_width > .01f || num_lines == 0) - i_new_line(data, ¤t_width, ¤t_height, ¤t_width_without_spaces, &num_lines, width, height); - break; - - case i_WORD_TYPE_NEW_LINE: - i_new_line(data, ¤t_width, ¤t_height, ¤t_width_without_spaces, &num_lines, width, height); - break; - - case i_WORD_TYPE_BLANCKS: - if (current_width_without_spaces > 0.f) - { - char_t word[128]; - real32_t word_width = 0.f, word_height = 0.f; - uint32_t size = (uint32_t)(next_text - ctext); - cassert(next_text > ctext); - str_copy_cn(word, 128, ctext, size); - word[size] = '\0'; - osgui_word_size(data, word, &word_width, &word_height); - if (current_width + word_width <= ref_width) - { - current_width += word_width; - if (word_height > current_height) - current_height = word_height; - } - else - { - i_new_line(data, ¤t_width, ¤t_height, ¤t_width_without_spaces, &num_lines, width, height); - } - } - break; - - case i_WORD_TYPE_TEXT: - { - char_t word[128]; - real32_t word_width = 0.f, word_height = 0.f; - uint32_t size = (uint32_t)(next_text - ctext); - cassert(next_text > ctext); - cassert(word_type == i_WORD_TYPE_TEXT); - str_copy_cn(word, 128, ctext, size); - word[size] = '\0'; - osgui_word_size(data, word, &word_width, &word_height); - - if (current_width + word_width <= ref_width) - { - current_width += word_width; - if (word_height > current_height) - current_height = word_height; - } - else - { - i_new_line(data, ¤t_width, ¤t_height, ¤t_width_without_spaces, &num_lines, width, height); - current_width = word_width; - current_height = word_height; - } - - current_width_without_spaces = current_width; - break; - } - - cassert_default(); - } - - ctext = next_text; - } - - *width = i_ceil(*width); - *height = i_ceil(*height); -} - -/*---------------------------------------------------------------------------*/ - gui_size_t osgui_size_font(const real32_t font_size) { if (font_size > font_regular_size() - 0.1f) diff --git a/src/osgui/osgui.inl b/src/osgui/osgui.inl index 244c903f..4ef35423 100644 --- a/src/osgui/osgui.inl +++ b/src/osgui/osgui.inl @@ -20,10 +20,6 @@ void osgui_finish_imp(void); Font *osgui_create_default_font(void); -void osgui_word_size(StringSizeData *data, const char_t *word, real32_t *width, real32_t *height); - -void osgui_text_bounds(StringSizeData *data, const char_t *text, const real32_t refwidth, real32_t *width, real32_t *height); - gui_size_t osgui_size_font(const real32_t font_size); vkey_t osgui_vkey_from_text(const char_t *text); diff --git a/src/osgui/osgui.ixx b/src/osgui/osgui.ixx index 5f5e737f..ef864595 100644 --- a/src/osgui/osgui.ixx +++ b/src/osgui/osgui.ixx @@ -21,7 +21,6 @@ typedef struct _osframe_t OSFrame; typedef struct _ostabstop_t OSTabStop; typedef struct _osscrolls_t OSScrolls; typedef struct _osscroll_t OSScroll; -typedef struct _strsize_data_t StringSizeData; struct _oshotkey_t { diff --git a/src/osgui/osx/osbutton.m b/src/osgui/osx/osbutton.m index 353d4798..70018271 100644 --- a/src/osgui/osx/osbutton.m +++ b/src/osgui/osx/osbutton.m @@ -999,11 +999,14 @@ void osbutton_image(OSButton *button, const Image *image) ptr_destopt(image_destroy, &cell->image, Image); if (image != NULL) { + cell->image = image_copy(image); [cell setImagePosition:NSImageLeft]; + _oscontrol_cell_set_image(cell, image); } else { [cell setImagePosition:NSNoImage]; + _oscontrol_cell_set_image(cell, NULL); } } else if (button_get_type(cell->flags) == ekBUTTON_FLAT || button_get_type(cell->flags) == ekBUTTON_FLATGLE) @@ -1066,9 +1069,15 @@ gui_state_t osbutton_get_state(const OSButton *button) void osbutton_vpadding(OSButton *button, const real32_t padding) { - cassert_no_null(button); - cassert(padding >= 0); - ((OSXButton *)button)->vpadding = (uint32_t)padding; + OSXButton *lbutton = cast(button, OSXButton); + cassert_no_null(lbutton); + if (button_get_type(lbutton->flags) == ekBUTTON_PUSH) + { + if (padding >= 0) + lbutton->vpadding = (uint32_t)padding; + else + lbutton->vpadding = UINT32_MAX; + } } /*---------------------------------------------------------------------------*/ @@ -1084,12 +1093,15 @@ void osbutton_bounds(const OSButton *button, const char_t *text, const real32_t { case ekBUTTON_PUSH: { + char_t tbuff[256]; real32_t margin_width, margin_height; OSXButtonCell *cell = [lbutton cell]; + real32_t xscale = font_xscale(lbutton->attrs.font); uint32_t imgwidth = 0; - _oscontrol_text_bounds(lbutton->attrs.font, text, -1.f, width, height); - _oscontrol_text_bounds(lbutton->attrs.font, "OO", -1.f, &margin_width, &margin_height); - *width += margin_width; + osgui_key_equivalent_text(text, tbuff, sizeof(tbuff)); + font_extents(lbutton->attrs.font, tbuff, -1.f, width, height); + font_extents(lbutton->attrs.font, xscale >= 1 ? "OO" : "OOOO", -1.f, &margin_width, &margin_height); + *width += margin_width + 2; cell->text_width = *width; if (cell->image != NULL) @@ -1099,17 +1111,17 @@ void osbutton_bounds(const OSButton *button, const char_t *text, const real32_t { case ekGUI_SIZE_REGULAR: if (imgwidth > 0) - *width += (real32_t)imgwidth + 4.f; + *width += (real32_t)imgwidth; *height = 22.f; break; case ekGUI_SIZE_SMALL: if (imgwidth > 0) - *width += (real32_t)imgwidth + 4.f; + *width += (real32_t)imgwidth; *height = 20.f; break; case ekGUI_SIZE_MINI: if (imgwidth > 0) - *width += (real32_t)imgwidth + 2.f; + *width += (real32_t)imgwidth; else *width += 2.f; *height = 16.f; @@ -1136,7 +1148,7 @@ void osbutton_bounds(const OSButton *button, const char_t *text, const real32_t /* Inexplicable crash in HighSierra and lowers */ static const real32_t i_CHECK_TEXT_SEP = 8.f; #endif - _oscontrol_text_bounds(lbutton->attrs.font, text, -1.f, width, height); + font_extents(lbutton->attrs.font, text, -1.f, width, height); switch (cell->size) { case ekGUI_SIZE_REGULAR: diff --git a/src/osgui/osx/oscombo.m b/src/osgui/osx/oscombo.m index 8ccfade1..7df51048 100644 --- a/src/osgui/osx/oscombo.m +++ b/src/osgui/osx/oscombo.m @@ -327,7 +327,7 @@ void oscombo_bounds(const OSCombo *combo, const real32_t refwidth, real32_t *wid cassert_no_null(combo); cassert_no_null(width); cassert_no_null(height); - _oscontrol_text_bounds(((OSXCombo *)combo)->attrs.font, "OO", -1.f, width, height); + font_extents(((OSXCombo *)combo)->attrs.font, "OO", -1.f, width, height); *width = refwidth; *height = 27.f; } diff --git a/src/osgui/osx/oscontrol.m b/src/osgui/osx/oscontrol.m index 06ba2170..efedc308 100644 --- a/src/osgui/osx/oscontrol.m +++ b/src/osgui/osx/oscontrol.m @@ -163,23 +163,10 @@ NSControlSize _oscontrol_control_size(const gui_size_t size) void _oscontrol_cell_set_image(NSCell *cell, const Image *image) { cassert_no_null(cell); - [cell setImage:(NSImage *)image_native(image)]; -} - -/*---------------------------------------------------------------------------*/ - -void _oscontrol_text_bounds(const Font *font, const char_t *text, const real32_t refwidth, real32_t *width, real32_t *height) -{ - uint32_t style; - StringSizeData data; - NSNumber *undertype = nil; - NSNumber *strikeout = nil; - style = font_style(font); - undertype = (style & ekFUNDERLINE) ? kUNDERLINE_STYLE_SINGLE : kUNDERLINE_STYLE_NONE; - strikeout = (style & ekFSTRIKEOUT) ? kUNDERLINE_STYLE_SINGLE : kUNDERLINE_STYLE_NONE; - data.dict = [[NSDictionary alloc] initWithObjectsAndKeys:(NSFont *)font_native(font), NSFontAttributeName, undertype, NSUnderlineStyleAttributeName, strikeout, NSStrikethroughStyleAttributeName, nil]; - osgui_text_bounds(&data, text, refwidth, width, height); - [data.dict release]; + if (image != NULL) + [cell setImage:(NSImage *)image_native(image)]; + else + [cell setImage:nil]; } /*---------------------------------------------------------------------------*/ diff --git a/src/osgui/osx/oscontrol_osx.inl b/src/osgui/osx/oscontrol_osx.inl index 7b09eb6e..a681c081 100644 --- a/src/osgui/osx/oscontrol_osx.inl +++ b/src/osgui/osx/oscontrol_osx.inl @@ -26,8 +26,6 @@ NSControlSize _oscontrol_control_size(const gui_size_t size); void _oscontrol_cell_set_image(NSCell *cell, const Image *image); -void _oscontrol_text_bounds(const Font *font, const char_t *text, const real32_t refwidth, real32_t *width, real32_t *height); - void _oscontrol_tooltip_set(NSView *view, const char_t *text); NSColor *_oscontrol_color(const color_t color); diff --git a/src/osgui/osx/osedit.m b/src/osgui/osx/osedit.m index 0075a03f..ec41fe77 100644 --- a/src/osgui/osx/osedit.m +++ b/src/osgui/osx/osedit.m @@ -75,6 +75,7 @@ @interface OSXEdit : NSView uint32_t flags; uint32_t vpadding; real32_t rpadding; + color_t bgcolor; NSRange select; CGFloat wpadding; OSTextAttr attrs; @@ -362,7 +363,7 @@ static void i_update_vpadding(OSXEdit *edit) uint32_t defpadding = 0; cassert_no_null(edit); - _oscontrol_text_bounds(edit->attrs.font, "OO", -1.f, &width, &height); + font_extents(edit->attrs.font, "OO", -1.f, &width, &height); defpadding = (uint32_t)((.3f * height) + .5f); if (defpadding % 2 == 1) @@ -406,6 +407,7 @@ static void i_update_vpadding(OSXEdit *edit) [field setCell:[[OSXTextFieldCell alloc] init]]; edit->editor = nil; edit->flags = flags; + edit->bgcolor = kCOLOR_DEFAULT; edit->vpadding = UINT32_MAX; edit->select = NSMakeRange(0, 0); edit->OnFilter = NULL; @@ -653,12 +655,26 @@ void osedit_color(OSEdit *edit, const color_t color) /*---------------------------------------------------------------------------*/ +static void i_set_bgcolor(OSXEdit *edit) +{ + NSColor *nscolor = nil; + cassert_no_null(edit); + if (edit->bgcolor != kCOLOR_DEFAULT) + nscolor = oscolor_NSColor(edit->bgcolor); + [edit->field setBackgroundColor:nscolor]; + if (edit->editor != nil) + [edit->editor setBackgroundColor:nscolor]; +} + +/*---------------------------------------------------------------------------*/ + void osedit_bgcolor(OSEdit *edit, const color_t color) { NSColor *nscolor = color != 0 ? oscolor_NSColor(color) : [NSColor textBackgroundColor]; OSXEdit *ledit = (OSXEdit *)edit; cassert_no_null(ledit); - [ledit->field setBackgroundColor:nscolor]; + ledit->bgcolor = color; + i_set_bgcolor(ledit); } /*---------------------------------------------------------------------------*/ @@ -684,7 +700,7 @@ void osedit_bounds(const OSEdit *edit, const real32_t refwidth, const uint32_t l if (lines == 1) { - _oscontrol_text_bounds(ledit->attrs.font, "OO", -1.f, width, height); + font_extents(ledit->attrs.font, "OO", -1.f, width, height); } else { @@ -695,7 +711,7 @@ void osedit_bounds(const OSEdit *edit, const real32_t refwidth, const uint32_t l for (i = 0; i < lines - 1; ++i) str_cat_c(text, 256, "O\n"); str_cat_c(text, 256, "O"); - _oscontrol_text_bounds(ledit->attrs.font, text, -1.f, width, height); + font_extents(ledit->attrs.font, text, -1.f, width, height); } *width = refwidth; @@ -829,6 +845,7 @@ void osedit_focus(OSEdit *edit, const bool_t focus) { NSWindow *window = [ledit->field window]; ledit->editor = [window fieldEditor:YES forObject:ledit->field]; + i_set_bgcolor(ledit); if (BIT_TEST(ledit->flags, ekEDIT_AUTOSEL) == TRUE) { diff --git a/src/osgui/osx/osglobals.m b/src/osgui/osx/osglobals.m index ed5a7ec1..b0f6d9ee 100644 --- a/src/osgui/osx/osglobals.m +++ b/src/osgui/osx/osglobals.m @@ -10,12 +10,14 @@ /* Operating System globals */ +#include "osgui.inl" #include "osgui_osx.inl" #include "osglobals.h" #include "osglobals.inl" #include "oscolor.inl" #include "oscontrol_osx.inl" #include +#include #include #include #include @@ -59,6 +61,7 @@ @interface OSXHeader : NSView static color_t i_GRID_COLOR; static color_t i_FOCUS_COLOR; static bool_t i_DARK_MODE = FALSE; + static ArrPt(Listener) *i_ONIDLES = NULL; DeclPt(Listener); diff --git a/src/osgui/osx/osgui_osx.m b/src/osgui/osx/osgui_osx.m index 81532227..9a78c6c3 100644 --- a/src/osgui/osx/osgui_osx.m +++ b/src/osgui/osx/osgui_osx.m @@ -188,21 +188,6 @@ void osgui_finish_imp(void) /*---------------------------------------------------------------------------*/ -void osgui_word_size(StringSizeData *data, const char_t *word, real32_t *width, real32_t *height) -{ - NSString *str = nil; - NSSize word_size; - cassert_no_null(data); - cassert_no_null(width); - cassert_no_null(height); - str = [NSString stringWithUTF8String:word]; - word_size = [str sizeWithAttributes:data->dict]; - *width = (real32_t)word_size.width; - *height = (real32_t)word_size.height; -} - -/*---------------------------------------------------------------------------*/ - void osgui_attach_menubar(OSWindow *window, OSMenu *menu) { cassert_no_null(menu); diff --git a/src/osgui/osx/oslabel.m b/src/osgui/osx/oslabel.m index e2ba7b65..62d5f843 100644 --- a/src/osgui/osx/oslabel.m +++ b/src/osgui/osx/oslabel.m @@ -155,7 +155,6 @@ - (void)drawRect:(NSRect)rect label->text = str_c(""); label->color = kCOLOR_DEFAULT; label->bgcolor = kCOLOR_DEFAULT; - /*draw_font(label->ctx, kFONT_DEFAULT);*/ draw_text_align(label->ctx, ekLEFT, ekTOP); draw_text_width(label->ctx, -1); draw_text_halign(label->ctx, ekLEFT); diff --git a/src/osgui/osx/ospopup.m b/src/osgui/osx/ospopup.m index e2478ced..e11396ba 100644 --- a/src/osgui/osx/ospopup.m +++ b/src/osgui/osx/ospopup.m @@ -241,7 +241,7 @@ void ospopup_bounds(const OSPopUp *popup, const char_t *text, real32_t *width, r cassert_no_null(lpopup); cassert_no_null(width); cassert_no_null(height); - _oscontrol_text_bounds(lpopup->attrs.font, text, -1.f, width, height); + font_extents(lpopup->attrs.font, text, -1.f, width, height); *width += 40.f; *height = 24.f; } diff --git a/src/osgui/win/osbutton.c b/src/osgui/win/osbutton.c index 8773d3d7..b51b7db3 100644 --- a/src/osgui/win/osbutton.c +++ b/src/osgui/win/osbutton.c @@ -552,8 +552,13 @@ gui_state_t osbutton_get_state(const OSButton *button) void osbutton_vpadding(OSButton *button, const real32_t padding) { cassert_no_null(button); - cassert(padding >= 0); - button->vpadding = (uint32_t)padding; + if (button_get_type(button->flags) == ekBUTTON_PUSH) + { + if (padding >= 0) + button->vpadding = (uint32_t)padding; + else + button->vpadding = UINT32_MAX; + } } /*---------------------------------------------------------------------------*/ @@ -571,15 +576,17 @@ void osbutton_bounds(const OSButton *button, const char_t *text, const real32_t { real32_t woff, hoff; real32_t fheight; - _oscontrol_text_bounds((const OSControl *)button, text, button->font, -1.f, width, &fheight); + font_extents(button->font, text, -1.f, width, &fheight); + /* Image is higher than text */ if (refheight > fheight) *height = refheight; else *height = fheight; - _oscontrol_text_bounds((const OSControl *)button, "O", button->font, -1.f, &woff, &hoff); + font_extents(button->font, "O", -1.f, &woff, &hoff); + /* Image width */ if (refwidth > 0.f) { *width += refwidth; @@ -611,7 +618,7 @@ void osbutton_bounds(const OSButton *button, const char_t *text, const real32_t case ekBUTTON_CHECK2: case ekBUTTON_CHECK3: case ekBUTTON_RADIO: - _oscontrol_text_bounds((const OSControl *)button, text, button->font, -1.f, width, height); + font_extents(button->font, text, -1.f, width, height); *width += (real32_t)GetSystemMetrics(SM_CXMENUCHECK); *width += (real32_t)GetSystemMetrics(SM_CXEDGE); *height = (real32_t)GetSystemMetrics(SM_CYMENUCHECK); diff --git a/src/osgui/win/oscontrol.cpp b/src/osgui/win/oscontrol.cpp index 558f1cd1..6ea6a871 100644 --- a/src/osgui/win/oscontrol.cpp +++ b/src/osgui/win/oscontrol.cpp @@ -219,23 +219,6 @@ void _oscontrol_update_font(OSControl *control, Font **current_font, const Font /*---------------------------------------------------------------------------*/ -void _oscontrol_text_bounds(const OSControl *control, const char_t *text, const Font *font, const real32_t refwidth, real32_t *width, real32_t *height) -{ - StringSizeData data; - HFONT current_font = NULL; - int ret = 0; - cassert_no_null(control); - data.hdc = GetDC(control->hwnd); - current_font = (HFONT)SelectObject(data.hdc, (HFONT)font_native(font)); - cassert_no_null(current_font); - osgui_text_bounds(&data, text, refwidth, width, height); - SelectObject(data.hdc, current_font); - ret = ReleaseDC(NULL, data.hdc); - cassert_unref(ret == 1, ret); -} - -/*---------------------------------------------------------------------------*/ - void _oscontrol_set_visible(OSControl *control, const bool_t visible) { cassert_no_null(control); diff --git a/src/osgui/win/oscontrol_win.inl b/src/osgui/win/oscontrol_win.inl index 7283464c..e81934c5 100644 --- a/src/osgui/win/oscontrol_win.inl +++ b/src/osgui/win/oscontrol_win.inl @@ -30,8 +30,6 @@ void _oscontrol_set_font(OSControl *control, const Font *font); void _oscontrol_update_font(OSControl *control, Font **current_font, const Font *font); -void _oscontrol_text_bounds(const OSControl *control, const char_t *text, const Font *font, const real32_t refwidth, real32_t *width, real32_t *height); - void _oscontrol_set_visible(OSControl *control, const bool_t visible); void _oscontrol_set_enabled(OSControl *control, const bool_t enabled); diff --git a/src/osgui/win/osedit.c b/src/osgui/win/osedit.c index 3eae9f36..e4db38be 100644 --- a/src/osgui/win/osedit.c +++ b/src/osgui/win/osedit.c @@ -110,7 +110,7 @@ static void i_update_vpadding(OSEdit *edit) uint32_t defpadding = 0; cassert_no_null(edit); - _oscontrol_text_bounds((OSControl *)edit, "O", edit->font, -1.f, &width, &height); + font_extents(edit->font, "O", -1.f, &width, &height); defpadding = (uint32_t)((.3f * height) + .5f); if (defpadding % 2 == 1) @@ -326,7 +326,7 @@ void osedit_bounds(const OSEdit *edit, const real32_t refwidth, const uint32_t l if (lines == 1) { - _oscontrol_text_bounds((OSControl *)edit, "O", edit->font, -1.f, width, height); + font_extents(edit->font, "O", -1.f, width, height); } else { @@ -337,7 +337,7 @@ void osedit_bounds(const OSEdit *edit, const real32_t refwidth, const uint32_t l for (i = 0; i < lines - 1; ++i) str_cat_c(text, 256, "O\n"); str_cat_c(text, 256, "O"); - _oscontrol_text_bounds((OSControl *)edit, text, edit->font, -1.f, width, height); + font_extents(edit->font, text, -1.f, width, height); } *width = refwidth; diff --git a/src/osgui/win/osgui_win.cpp b/src/osgui/win/osgui_win.cpp index 4b9f2f76..e903dff7 100644 --- a/src/osgui/win/osgui_win.cpp +++ b/src/osgui/win/osgui_win.cpp @@ -899,23 +899,3 @@ void osgui_pre_initialize_imp(void) { cassert(FALSE); } - -/*---------------------------------------------------------------------------*/ - -void osgui_word_size(StringSizeData *data, const char_t *word, real32_t *width, real32_t *height) -{ - SIZE word_size; - uint32_t num_chars = 0, num_bytes = 0; - WCHAR wword[256]; - BOOL ret = 0; - cassert_no_null(data); - cassert_no_null(width); - cassert_no_null(height); - num_chars = unicode_nchars(word, ekUTF8); - num_bytes = unicode_convers(word, (char_t *)wword, ekUTF8, ekUTF16, sizeof(wword)); - cassert_unref(num_bytes < sizeof(wword), num_bytes); - ret = GetTextExtentPoint32(data->hdc, wword, (int)num_chars, &word_size); - cassert_unref(ret != 0, ret); - *width = (real32_t)word_size.cx; - *height = (real32_t)word_size.cy; -} diff --git a/src/osgui/win/osgui_win.ixx b/src/osgui/win/osgui_win.ixx index 23e4db30..1e744c42 100644 --- a/src/osgui/win/osgui_win.ixx +++ b/src/osgui/win/osgui_win.ixx @@ -73,11 +73,6 @@ struct _oscontrol_t int32_t y; }; -struct _strsize_data_t -{ - HDC hdc; -}; - struct _osdraw_t { HTHEME button_theme; diff --git a/src/osgui/win/oslabel.c b/src/osgui/win/oslabel.c index 540d7383..fed8d381 100644 --- a/src/osgui/win/oslabel.c +++ b/src/osgui/win/oslabel.c @@ -256,7 +256,7 @@ void oslabel_bgcolor(OSLabel *label, const color_t color) void oslabel_bounds(const OSLabel *label, const char_t *text, const real32_t refwidth, real32_t *width, real32_t *height) { cassert_no_null(label); - _oscontrol_text_bounds((OSControl *)label, text, label->font, refwidth, width, height); + font_extents(label->font, text, refwidth, width, height); } /*---------------------------------------------------------------------------*/ diff --git a/src/osgui/win/ospopup.c b/src/osgui/win/ospopup.c index f0e2da4e..f98d7c7e 100644 --- a/src/osgui/win/ospopup.c +++ b/src/osgui/win/ospopup.c @@ -196,7 +196,7 @@ void ospopup_bounds(const OSPopUp *popup, const char_t *text, real32_t *width, r cassert_no_null(popup); cassert_no_null(width); cassert_no_null(height); - _oscontrol_text_bounds((const OSControl *)popup, text, popup->font, -1.f, width, height); + font_extents(popup->font, text, -1.f, width, height); *width += 40.f; *height = 24.f;