Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright 2010 Vincent Sanders <vince@simtec.co.uk>
  3.  *
  4.  * Framebuffer windowing toolkit scrollbar widgets.
  5.  *
  6.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  7.  *
  8.  * NetSurf is free software; you can redistribute it and/or modify
  9.  * it under the terms of the GNU General Public License as published by
  10.  * the Free Software Foundation; version 2 of the License.
  11.  *
  12.  * NetSurf is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.  * GNU General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU General Public License
  18.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  19.  */
  20.  
  21. #include <stdbool.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24.  
  25. #include <libnsfb.h>
  26. #include <libnsfb_plot.h>
  27. #include <libnsfb_plot_util.h>
  28. #include <libnsfb_event.h>
  29.  
  30. #include "utils/log.h"
  31. #include "desktop/browser.h"
  32. #include "render/font.h"
  33.  
  34. #include "framebuffer/gui.h"
  35. #include "framebuffer/fbtk.h"
  36. #include "framebuffer/font.h"
  37. #include "framebuffer/framebuffer.h"
  38. #include "framebuffer/image_data.h"
  39.  
  40. #include "widget.h"
  41.  
  42. //#define TEXT_WIDGET_BORDER 3 /**< The pixel border round a text widget. */
  43.  
  44. /* Lighten a colour by taking seven eights of each channel's intensity
  45.  * and adding a full eighth
  46.  */
  47. #define brighten_colour(c1)                                     \
  48.         (((((7 * ((c1 >> 16) & 0xff)) >> 3) + 32) << 16) |      \
  49.          ((((7 * ((c1 >> 8) & 0xff)) >> 3) + 32) << 8) |        \
  50.          ((((7 * (c1 & 0xff)) >> 3) + 32) << 0))
  51.  
  52. /* Convert pixels to points, assuming a DPI of 90 */
  53. #define px_to_pt(x) (((x) * 72) / FBTK_DPI)
  54.  
  55. /* Get a font style for a text input */
  56. static inline void
  57. fb_text_font_style(fbtk_widget_t *widget, int *font_height, int *padding,
  58.                 plot_font_style_t *font_style)
  59. {
  60.         if (widget->u.text.outline)
  61.                 *padding = 1;
  62.         else
  63.                 *padding = 0;
  64.  
  65. #ifdef FB_USE_FREETYPE
  66.         *padding += widget->height / 6;
  67.         *font_height = widget->height - *padding - *padding;
  68. #else
  69.         *font_height = font_regular.height;
  70.         *padding = (widget->height - *padding - *font_height) / 2;
  71. #endif
  72.  
  73.         font_style->family = PLOT_FONT_FAMILY_SANS_SERIF;
  74.         font_style->size = px_to_pt(*font_height) * FONT_SIZE_SCALE;
  75.         font_style->weight = 400;
  76.         font_style->flags = FONTF_NONE;
  77.         font_style->background = widget->bg;
  78.         font_style->foreground = widget->fg;
  79. }
  80.  
  81. /** Text redraw callback.
  82.  *
  83.  * Called when a text widget requires redrawing.
  84.  *
  85.  * @param widget The widget to be redrawn.
  86.  * @param cbi The callback parameters.
  87.  * @return The callback result.
  88.  */
  89. static int
  90. fb_redraw_text(fbtk_widget_t *widget, fbtk_callback_info *cbi )
  91. {
  92.         nsfb_bbox_t bbox;
  93.         nsfb_bbox_t rect;
  94.         fbtk_widget_t *root;
  95.         plot_font_style_t font_style;
  96.         int caret_x, caret_y, caret_h;
  97.         int fh;
  98.         int padding;
  99.         int scroll = 0;
  100.         bool caret = false;
  101.  
  102.         fb_text_font_style(widget, &fh, &padding, &font_style);
  103.  
  104.         if (fbtk_get_caret(widget, &caret_x, &caret_y, &caret_h)) {
  105.                 caret = true;
  106.         }
  107.  
  108.         root = fbtk_get_root_widget(widget);
  109.  
  110.         fbtk_get_bbox(widget, &bbox);
  111.  
  112.         rect = bbox;
  113.  
  114.         nsfb_claim(root->u.root.fb, &bbox);
  115.  
  116.         /* clear background */
  117.         if ((widget->bg & 0xFF000000) != 0) {
  118.                 /* transparent polygon filling isnt working so fake it */
  119.                 nsfb_plot_rectangle_fill(root->u.root.fb, &bbox, widget->bg);
  120.         }
  121.  
  122.         /* widget can have a single pixel outline border */
  123.         if (widget->u.text.outline) {
  124.                 rect.x1--;
  125.                 rect.y1--;
  126.                 nsfb_plot_rectangle(root->u.root.fb, &rect, 1,
  127.                                 0x00000000, false, false);
  128.         }
  129.  
  130.         if (widget->u.text.text != NULL) {
  131.                 int x = bbox.x0 + padding;
  132.                 int y = bbox.y0 + ((fh * 3 + 2) / 4) + padding;
  133.  
  134. #ifdef FB_USE_FREETYPE
  135.                 /* Freetype renders text higher */
  136.                 y += 1;
  137. #endif
  138.  
  139.                 if (caret && widget->width - padding - padding < caret_x) {
  140.                         scroll = (widget->width - padding - padding) - caret_x;
  141.                         x +=  scroll;
  142.                 }
  143.  
  144.                 /* Call the fb text plotting, baseline is 3/4 down the font */
  145.                 fb_plotters.text(x, y, widget->u.text.text,
  146.                                 widget->u.text.len, &font_style);
  147.         }
  148.  
  149.         if (caret) {
  150.                 /* This widget has caret, so render it */
  151.                 nsfb_t *nsfb = fbtk_get_nsfb(widget);
  152.                 nsfb_bbox_t line;
  153.                 nsfb_plot_pen_t pen;
  154.  
  155.                 line.x0 = bbox.x0 + caret_x + scroll;
  156.                 line.y0 = bbox.y0 + caret_y;
  157.                 line.x1 = bbox.x0 + caret_x + scroll;
  158.                 line.y1 = bbox.y0 + caret_y + caret_h;
  159.  
  160.                 pen.stroke_type = NFSB_PLOT_OPTYPE_SOLID;
  161.                 pen.stroke_width = 1;
  162.                 pen.stroke_colour = 0xFF0000FF;
  163.  
  164.                 nsfb_plot_line(nsfb, &line, &pen);
  165.         }
  166.  
  167.         nsfb_update(root->u.root.fb, &bbox);
  168.  
  169.         return 0;
  170. }
  171.  
  172. /** Text button redraw callback.
  173.  *
  174.  * Called when a text widget requires redrawing.
  175.  *
  176.  * @param widget The widget to be redrawn.
  177.  * @param cbi The callback parameters.
  178.  * @return The callback result.
  179.  */
  180. static int
  181. fb_redraw_text_button(fbtk_widget_t *widget, fbtk_callback_info *cbi )
  182. {
  183.         nsfb_bbox_t bbox;
  184.         nsfb_bbox_t rect;
  185.         nsfb_bbox_t line;
  186.         nsfb_plot_pen_t pen;
  187.         plot_font_style_t font_style;
  188.         int fh;
  189.         int border;
  190.         fbtk_widget_t *root = fbtk_get_root_widget(widget);
  191.  
  192.         fb_text_font_style(widget, &fh, &border, &font_style);
  193.  
  194.         pen.stroke_type = NFSB_PLOT_OPTYPE_SOLID;
  195.         pen.stroke_width = 1;
  196.         pen.stroke_colour = brighten_colour(widget->bg);
  197.  
  198.         fbtk_get_bbox(widget, &bbox);
  199.  
  200.         rect = bbox;
  201.         rect.x1--;
  202.         rect.y1--;
  203.  
  204.         nsfb_claim(root->u.root.fb, &bbox);
  205.  
  206.         /* clear background */
  207.         if ((widget->bg & 0xFF000000) != 0) {
  208.                 /* transparent polygon filling isnt working so fake it */
  209.                 nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg);
  210.         }
  211.  
  212.         if (widget->u.text.outline) {
  213.                 line.x0 = rect.x0;
  214.                 line.y0 = rect.y0;
  215.                 line.x1 = rect.x0;
  216.                 line.y1 = rect.y1;
  217.                 nsfb_plot_line(root->u.root.fb, &line, &pen);
  218.                 line.x0 = rect.x0;
  219.                 line.y0 = rect.y0;
  220.                 line.x1 = rect.x1;
  221.                 line.y1 = rect.y0;
  222.                 nsfb_plot_line(root->u.root.fb, &line, &pen);
  223.                 pen.stroke_colour = darken_colour(widget->bg);
  224.                 line.x0 = rect.x0;
  225.                 line.y0 = rect.y1;
  226.                 line.x1 = rect.x1;
  227.                 line.y1 = rect.y1;
  228.                 nsfb_plot_line(root->u.root.fb, &line, &pen);
  229.                 line.x0 = rect.x1;
  230.                 line.y0 = rect.y0;
  231.                 line.x1 = rect.x1;
  232.                 line.y1 = rect.y1;
  233.                 nsfb_plot_line(root->u.root.fb, &line, &pen);
  234.         }
  235.  
  236.         if (widget->u.text.text != NULL) {
  237.                 /* Call the fb text plotting, baseline is 3/4 down the font */
  238.                 fb_plotters.text(bbox.x0 + border,
  239.                                 bbox.y0 + ((fh * 3) / 4) + border,
  240.                                 widget->u.text.text,
  241.                                 widget->u.text.len,
  242.                                 &font_style);
  243.         }
  244.  
  245.         nsfb_update(root->u.root.fb, &bbox);
  246.  
  247.         return 0;
  248. }
  249.  
  250. static void
  251. fb_text_input_remove_caret_cb(fbtk_widget_t *widget)
  252. {
  253.         int c_x, c_y, c_h;
  254.  
  255.         if (fbtk_get_caret(widget, &c_x, &c_y, &c_h)) {
  256.                 fbtk_request_redraw(widget);
  257.         }
  258. }
  259.  
  260. /** Routine called when text events occour in writeable widget.
  261.  *
  262.  * @param widget The widget reciving input events.
  263.  * @param cbi The callback parameters.
  264.  * @return The callback result.
  265.  */
  266. static int
  267. text_input(fbtk_widget_t *widget, fbtk_callback_info *cbi)
  268. {
  269.         int value;
  270.         static fbtk_modifier_type modifier = FBTK_MOD_CLEAR;
  271.         char *temp;
  272.         plot_font_style_t font_style;
  273.         int fh;
  274.         int border;
  275.         bool caret_moved = false;
  276.  
  277.         fb_text_font_style(widget, &fh, &border, &font_style);
  278.  
  279.         if (cbi->event == NULL) {
  280.                 /* gain focus */
  281.                 if (widget->u.text.text == NULL)
  282.                         widget->u.text.text = calloc(1,1);
  283.  
  284.                 return 0;
  285.         }
  286.  
  287.         value = cbi->event->value.keycode;
  288.  
  289.         if (cbi->event->type != NSFB_EVENT_KEY_DOWN) {
  290.                 switch (value) {
  291.                 case NSFB_KEY_RSHIFT:
  292.                         modifier &= ~FBTK_MOD_RSHIFT;
  293.                         break;
  294.  
  295.                 case NSFB_KEY_LSHIFT:
  296.                         modifier &= ~FBTK_MOD_LSHIFT;
  297.                         break;
  298.  
  299.                 case NSFB_KEY_RCTRL:
  300.                         modifier &= ~FBTK_MOD_RCTRL;
  301.                         break;
  302.  
  303.                 case NSFB_KEY_LCTRL:
  304.                         modifier &= ~FBTK_MOD_LCTRL;
  305.                         break;
  306.  
  307.                 default:
  308.                         break;
  309.                 }
  310.                 return 0;
  311.         }
  312.  
  313.         switch (value) {
  314.         case NSFB_KEY_BACKSPACE:
  315.                 if (widget->u.text.idx <= 0)
  316.                         break;
  317.                 memmove(widget->u.text.text + widget->u.text.idx - 1,
  318.                                 widget->u.text.text + widget->u.text.idx,
  319.                                 widget->u.text.len - widget->u.text.idx);
  320.                 widget->u.text.idx--;
  321.                 widget->u.text.len--;
  322.                 widget->u.text.text[widget->u.text.len] = 0;
  323.  
  324.                 nsfont.font_width(&font_style, widget->u.text.text,
  325.                                 widget->u.text.len, &widget->u.text.width);
  326.  
  327.                 caret_moved = true;
  328.                 break;
  329.  
  330.         case NSFB_KEY_RETURN:
  331.                 widget->u.text.enter(widget->u.text.pw, widget->u.text.text);
  332.                 break;
  333.  
  334.         case NSFB_KEY_RIGHT:
  335.                 if (widget->u.text.idx < widget->u.text.len) {
  336.                         if (modifier == FBTK_MOD_CLEAR)
  337.                                 widget->u.text.idx++;
  338.                         else
  339.                                 widget->u.text.idx = widget->u.text.len;
  340.  
  341.                         caret_moved = true;
  342.                 }
  343.                 break;
  344.  
  345.         case NSFB_KEY_LEFT:
  346.                 if (widget->u.text.idx > 0) {
  347.                         if (modifier == FBTK_MOD_CLEAR)
  348.                                 widget->u.text.idx--;
  349.                         else
  350.                                 widget->u.text.idx = 0;
  351.  
  352.                         caret_moved = true;
  353.                 }
  354.                 break;
  355.  
  356.         case NSFB_KEY_PAGEUP:
  357.         case NSFB_KEY_PAGEDOWN:
  358.         case NSFB_KEY_UP:
  359.         case NSFB_KEY_DOWN:
  360.                 /* Not handling any of these correctly yet, but avoid putting
  361.                  * charcters in the text widget when they're pressed. */
  362.                 break;
  363.  
  364.         case NSFB_KEY_RSHIFT:
  365.                 modifier |= FBTK_MOD_RSHIFT;
  366.                 break;
  367.  
  368.         case NSFB_KEY_LSHIFT:
  369.                 modifier |= FBTK_MOD_LSHIFT;
  370.                 break;
  371.  
  372.         case NSFB_KEY_RCTRL:
  373.                 modifier |= FBTK_MOD_RCTRL;
  374.                 break;
  375.  
  376.         case NSFB_KEY_LCTRL:
  377.                 modifier |= FBTK_MOD_LCTRL;
  378.                 break;
  379.  
  380.         default:
  381.                 if (modifier & FBTK_MOD_LCTRL || modifier & FBTK_MOD_RCTRL) {
  382.                         /* CTRL pressed, don't enter any text */
  383.                         if (value == NSFB_KEY_u) {
  384.                                 /* CTRL+U: clear writable */
  385.                                 widget->u.text.idx = 0;
  386.                                 widget->u.text.len = 0;
  387.                                 widget->u.text.text[widget->u.text.len] = '\0';
  388.                                 widget->u.text.width = 0;
  389.                                 caret_moved = true;
  390.                         }
  391.                         break;
  392.                 }
  393.  
  394.                 /* allow for new character and null */
  395.                 temp = realloc(widget->u.text.text, widget->u.text.len + 2);
  396.                 if (temp == NULL) {
  397.                         break;
  398.                 }
  399.  
  400.                 widget->u.text.text = temp;
  401.                 memmove(widget->u.text.text + widget->u.text.idx + 1,
  402.                                 widget->u.text.text + widget->u.text.idx,
  403.                                 widget->u.text.len - widget->u.text.idx);
  404.                 widget->u.text.text[widget->u.text.idx] =
  405.                                 fbtk_keycode_to_ucs4(value, modifier);
  406.                 widget->u.text.idx++;
  407.                 widget->u.text.len++;
  408.                 widget->u.text.text[widget->u.text.len] = '\0';
  409.  
  410.                 nsfont.font_width(&font_style, widget->u.text.text,
  411.                                 widget->u.text.len, &widget->u.text.width);
  412.                 caret_moved = true;
  413.                 break;
  414.         }
  415.  
  416.         if (caret_moved) {
  417.                 nsfont.font_width(&font_style, widget->u.text.text,
  418.                                 widget->u.text.idx, &widget->u.text.idx_offset);
  419.                 fbtk_set_caret(widget, true,
  420.                                 widget->u.text.idx_offset + border,
  421.                                 border,
  422.                                 widget->height - border - border,
  423.                                 fb_text_input_remove_caret_cb);
  424.         }
  425.  
  426.         fbtk_request_redraw(widget);
  427.  
  428.         return 0;
  429. }
  430.  
  431. /** Routine called when click events occour in writeable widget.
  432.  *
  433.  * @param widget The widget reciving click events.
  434.  * @param cbi The callback parameters.
  435.  * @return The callback result.
  436.  */
  437. static int
  438. text_input_click(fbtk_widget_t *widget, fbtk_callback_info *cbi)
  439. {
  440.         plot_font_style_t font_style;
  441.         int fh;
  442.         int border;
  443.         size_t idx;
  444.  
  445.         fb_text_font_style(widget, &fh, &border, &font_style);
  446.  
  447.         widget->u.text.idx = widget->u.text.len;
  448.  
  449.         nsfont.font_position_in_string(&font_style, widget->u.text.text,
  450.                         widget->u.text.len, cbi->x - border,
  451.                         &idx,
  452.                         &widget->u.text.idx_offset);
  453.         widget->u.text.idx = idx;
  454.         fbtk_set_caret(widget, true,
  455.                         widget->u.text.idx_offset + border,
  456.                         border,
  457.                         widget->height - border - border,
  458.                         fb_text_input_remove_caret_cb);
  459.  
  460.         fbtk_request_redraw(widget);
  461.  
  462.         return 0;
  463. }
  464.  
  465. /** Routine called when "stripped of focus" event occours for writeable widget.
  466.  *
  467.  * @param widget The widget reciving "stripped of focus" event.
  468.  * @param cbi The callback parameters.
  469.  * @return The callback result.
  470.  */
  471. static int
  472. text_input_strip_focus(fbtk_widget_t *widget, fbtk_callback_info *cbi)
  473. {
  474.         fbtk_set_caret(widget, false, 0, 0, 0, NULL);
  475.  
  476.         return 0;
  477. }
  478.  
  479. /* exported function documented in fbtk.h */
  480. void
  481. fbtk_writable_text(fbtk_widget_t *widget, fbtk_enter_t enter, void *pw)
  482. {
  483.         widget->u.text.enter = enter;
  484.         widget->u.text.pw = pw;
  485.  
  486.         fbtk_set_handler(widget, FBTK_CBT_INPUT, text_input, widget);
  487. }
  488.  
  489. /* exported function documented in fbtk.h */
  490. void
  491. fbtk_set_text(fbtk_widget_t *widget, const char *text)
  492. {
  493.         plot_font_style_t font_style;
  494.         int c_x, c_y, c_h;
  495.         int fh;
  496.         int border;
  497.  
  498.         if ((widget == NULL) || (widget->type != FB_WIDGET_TYPE_TEXT))
  499.                 return;
  500.         if (widget->u.text.text != NULL) {
  501.                 if (strcmp(widget->u.text.text, text) == 0)
  502.                         return; /* text is being set to the same thing */
  503.                 free(widget->u.text.text);
  504.         }
  505.         widget->u.text.text = strdup(text);
  506.         widget->u.text.len = strlen(text);
  507.         widget->u.text.idx = widget->u.text.len;
  508.  
  509.  
  510.         fb_text_font_style(widget, &fh, &border, &font_style);
  511.         nsfont.font_width(&font_style, widget->u.text.text,
  512.                         widget->u.text.len, &widget->u.text.width);
  513.         nsfont.font_width(&font_style, widget->u.text.text,
  514.                         widget->u.text.idx, &widget->u.text.idx_offset);
  515.  
  516.         if (fbtk_get_caret(widget, &c_x, &c_y, &c_h)) {
  517.                 /* Widget has caret; move it to end of new string */
  518.                 fbtk_set_caret(widget, true,
  519.                                 widget->u.text.idx_offset + border,
  520.                                 border,
  521.                                 widget->height - border - border,
  522.                                 fb_text_input_remove_caret_cb);
  523.         }
  524.  
  525.         fbtk_request_redraw(widget);
  526. }
  527.  
  528. /* exported function documented in fbtk.h */
  529. fbtk_widget_t *
  530. fbtk_create_text(fbtk_widget_t *parent,
  531.                  int x,
  532.                  int y,
  533.                  int width,
  534.                  int height,
  535.                  colour bg,
  536.                  colour fg,
  537.                  bool outline)
  538. {
  539.         fbtk_widget_t *neww;
  540.  
  541.         neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_TEXT, x, y, width, height);
  542.         neww->fg = fg;
  543.         neww->bg = bg;
  544.         neww->mapped = true;
  545.         neww->u.text.outline = outline;
  546.  
  547.         fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_text, NULL);
  548.  
  549.         return neww;
  550. }
  551.  
  552. /* exported function documented in fbtk.h */
  553. fbtk_widget_t *
  554. fbtk_create_writable_text(fbtk_widget_t *parent,
  555.                           int x,
  556.                           int y,
  557.                           int width,
  558.                           int height,
  559.                           colour bg,
  560.                           colour fg,
  561.                           bool outline,
  562.                           fbtk_enter_t enter,
  563.                           void *pw)
  564. {
  565.         fbtk_widget_t *neww;
  566.  
  567.         neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_TEXT, x, y, width, height);
  568.         neww->fg = fg;
  569.         neww->bg = bg;
  570.         neww->mapped = true;
  571.  
  572.         neww->u.text.outline = outline;
  573.         neww->u.text.enter = enter;
  574.         neww->u.text.pw = pw;
  575.  
  576.         fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_text, NULL);
  577.         fbtk_set_handler(neww, FBTK_CBT_CLICK, text_input_click, pw);
  578.         fbtk_set_handler(neww, FBTK_CBT_STRIP_FOCUS, text_input_strip_focus, NULL);
  579.         fbtk_set_handler(neww, FBTK_CBT_INPUT, text_input, neww);
  580.  
  581.         return neww;
  582. }
  583.  
  584. /* exported function documented in fbtk.h */
  585. fbtk_widget_t *
  586. fbtk_create_text_button(fbtk_widget_t *parent,
  587.                         int x,
  588.                         int y,
  589.                         int width,
  590.                         int height,
  591.                         colour bg,
  592.                         colour fg,
  593.                         fbtk_callback click,
  594.                         void *pw)
  595. {
  596.         fbtk_widget_t *neww;
  597.  
  598.         neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_TEXT, x, y, width, height);
  599.         neww->fg = fg;
  600.         neww->bg = bg;
  601.         neww->mapped = true;
  602.  
  603.         neww->u.text.outline = true;
  604.  
  605.         fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_text_button, NULL);
  606.         fbtk_set_handler(neww, FBTK_CBT_CLICK, click, pw);
  607.         fbtk_set_handler(neww, FBTK_CBT_POINTERENTER, fbtk_set_ptr, &hand_image);
  608.  
  609.         return neww;
  610. }
  611.  
  612. /*
  613.  * Local Variables:
  614.  * c-basic-offset:8
  615.  * End:
  616.  */
  617.