indexing description: "[ A box that holds multi-line text than can be aligned and resized. Vertical alignment only matters, if the height of the text is smaller than the height of the box itself or if Align_user_specified is used. With Align_user_specified for vertical alignment, the baseline of the first line will be aligned with the upper boundary of the box. If a size is specified during creation (make), the text will be line-wrapped to fit within the given width. If the text cannot be broken-up to satisfy this constraint, the box will simply be resized. If no size is given during creation (make_auto_sized), the size of the box will be determined by the text itself. ]" author: "" date: "$Date$" revision: "$Revision$" class EM_VIZ_TEXT_BOX inherit EM_VIZ_BOX redefine vz_cache, update end EM_VIZ_RESIZEABLE EM_VIZ_ALIGNABLE create make, make_auto_sized feature -- Initialization make (a_suggested_size: like size; a_text: like text; a_format: like format) is -- Initialize with `a_suggested_size', `a_text' and `a_format' for the text require text_exists: a_text /= Void format_exists: a_format /= Void width_not_negative: 0.0 <= a_suggested_size.x do make_box make_resizeable (a_suggested_size) make_alignable create_internals enable_line_wrapping set_text (a_text) set_format (a_format) ensure all_set: text = a_text and format = a_format lines_exist: lines /= Void end make_auto_sized (a_text: like text; a_format: like format) is -- Make new text box with `a_text' and `a_format' -- Line-wrapping is disabled require text_exists: a_text /= Void format_exists: a_format /= Void do make ([0,0,0], a_text, a_format) disable_line_wrapping ensure no_wrapping: not line_wrapping_enabled end feature -- Access line_wrapping_enabled: BOOLEAN -- Should lines be broken up to fit? format: EM3D_FONT_FORMAT -- Font format used for rendering text: STRING -- Raw text feature -- Element change enable_line_wrapping is -- Enable line wrapping do line_wrapping_enabled := True expire ensure expired: needs_update enabled: line_wrapping_enabled end disable_line_wrapping is -- Disable line wrapping do line_wrapping_enabled := False expire ensure expired: needs_update disabled: not line_wrapping_enabled end set_text (a_text: like text) is -- Set text to `a_text' and expire require text_exists: a_text /= Void do text := a_text expire ensure set: text = a_text expired: needs_update end set_format (a_format: like format) is -- Set font format to `a_format' and expire require format_exists: a_format /= Void do format := a_format expire ensure set: format = a_format expired: needs_update end feature -- Update mechanism update is -- Force update do update_lines update_dimensions Precursor end feature {NONE} -- Implementation vz_cache is -- Cache lines, needed because EM3D_TTF_FONT -- needs to buffer each glyph first before we can compile -- any display lists do from lines.start until lines.off loop lines.item_for_iteration.cache lines.forth end end vz_render (viewing_direction: EM_VECTOR3D) is -- Render strings local l: EM3D_STRING ih, iw: EM_INTERVAL x, y, z: DOUBLE do ih := bounds.y ih.resize (format.line_skip * lines.count) ih.translate (-format.baseline_offset) ih.align_with (bounds.y, vertical_alignment) from y := ih.min + format.baseline_offset z := bounds.z.align_value (0.0, depth_alignment) lines.start until lines.off loop l := lines.item_for_iteration iw := bounds.x iw.resize (l.width) iw.align_with (bounds.x, horizontal_alignment) x := iw.min vz_push_frame vz_translate (vz_snap_point ([x, y, z])) l.render ([0,0,0]) vz_pop_frame y := y + format.line_skip lines.forth end end optimal_size: like size is -- Optimal size for current configuration of text local max_width: DOUBLE do from max_width := 0.0 lines.start until lines.off loop max_width := max_width.max (lines.item_for_iteration.width) lines.forth end Result.set (max_width, lines.count * format.line_skip, depth) end update_dimensions is -- Update dimension (resize if neccessary) local s: like size do s := size s.ew_maximum (optimal_size) set_size_internal (s) end update_lines is -- Split text into lines into words -- and spread the word from paragraphs over lines local raw: LIST [STRING] do paragraphs.wipe_out lines.wipe_out raw := text.split ('%N') if line_wrapping_enabled then from raw.start until raw.off loop raw.item.replace_substring_all ("%T", " ") paragraphs.put_last (raw.item.split (' ')) raw.forth end from paragraphs.start until paragraphs.off loop split_paragraph_into_lines (paragraphs.item_for_iteration) paragraphs.forth end else from raw.start until raw.off loop raw.item.replace_substring_all ("%T", " ") put_line (raw.item) raw.forth end end end split_paragraph_into_lines (p: LIST [STRING]) is -- Split paragraph into one or more lines according to width local accus: STRING accuw: DOUBLE wordw: DOUBLE space: DOUBLE do -- Ah the beauty of Eiffel. Ocaml is nothing... from space := format.char_width (' ') create accus.make (0) accuw := 0 p.start until p.off loop wordw := format.string_width (p.item) if accuw + wordw > width then if accuw = 0.0 then -- Word is too long anyway, just make one line out of it put_line (p.item) create accus.make (0) accuw := 0.0 else -- New line and word goes to next line put_line (accus) create accus.make_from_string (p.item) accuw := wordw + space end else -- Fits perfectly onto current line if accuw > 0.0 then accus.append_character (' ') end accus.append (p.item) accuw := accuw + wordw + space end p.forth end if accuw > 0.0 then put_line (accus) end end put_line (s: STRING) is -- Insert a line local line: EM3D_STRING do create line.make_with_format (s, format) lines.put_last (line) end create_internals is -- Create internal state do create paragraphs.make create lines.make end paragraphs: DS_LINKED_LIST [LIST [STRING]] -- List of list of words (strings) lines: DS_LINKED_LIST [EM3D_STRING] -- List of lines for rendering end