Merge branch 'harfbuzz' into new-theme

This commit is contained in:
David Capello 2017-02-21 18:07:11 -03:00
commit 776566463b
26 changed files with 758 additions and 401 deletions

3
.gitmodules vendored
View File

@ -42,3 +42,6 @@
[submodule "third_party/cmark"]
path = third_party/cmark
url = https://github.com/aseprite/cmark.git
[submodule "third_party/harfbuzz"]
path = third_party/harfbuzz
url = https://github.com/aseprite/harfbuzz.git

View File

@ -1,5 +1,5 @@
# Aseprite
# Copyright (C) 2001-2016 David Capello
# Copyright (C) 2001-2017 David Capello
#
# Parts of this file come from the Allegro 4.4 CMakeLists.txt
@ -143,6 +143,7 @@ set(LOADPNG_DIR ${CMAKE_SOURCE_DIR}/third_party/loadpng)
set(LIBWEBP_DIR ${CMAKE_SOURCE_DIR}/third_party/libwebp)
set(PIXMAN_DIR ${CMAKE_SOURCE_DIR}/third_party/pixman)
set(FREETYPE_DIR ${CMAKE_SOURCE_DIR}/third_party/freetype2)
set(HARFBUZZ_DIR ${CMAKE_SOURCE_DIR}/third_party/harfbuzz)
set(SIMPLEINI_DIR ${CMAKE_SOURCE_DIR}/third_party/simpleini)
set(TINYXML_DIR ${CMAKE_SOURCE_DIR}/third_party/tinyxml)
set(ZLIB_DIR ${CMAKE_SOURCE_DIR}/third_party/zlib)
@ -263,6 +264,10 @@ else()
endif()
include_directories(${FREETYPE_INCLUDE_DIRS})
# harfbuzz
set(HARFBUZZ_LIBRARIES harfbuzz)
set(HARFBUZZ_INCLUDE_DIRS ${HARFBUZZ_DIR}/src)
if(USE_SHARED_GIFLIB)
find_package(GIF REQUIRED)
else()

View File

@ -486,6 +486,47 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
```
# [harfbuzz](http://harfbuzz.org)
```
HarfBuzz is licensed under the so-called "Old MIT" license. Details follow.
For parts of HarfBuzz that are licensed under different licenses see individual
files names COPYING in subdirectories where applicable.
Copyright © 2010,2011,2012 Google, Inc.
Copyright © 2012 Mozilla Foundation
Copyright © 2011 Codethink Limited
Copyright © 2008,2010 Nokia Corporation and/or its subsidiary(-ies)
Copyright © 2009 Keith Stribley
Copyright © 2009 Martin Hosken and SIL International
Copyright © 2007 Chris Wilson
Copyright © 2006 Behdad Esfahbod
Copyright © 2005 David Turner
Copyright © 2004,2007,2008,2009,2010 Red Hat, Inc.
Copyright © 1998-2004 David Turner and Werner Lemberg
For full copyright notices consult the individual files in the package.
Permission is hereby granted, without written agreement and without
license or royalty fees, to use, copy, modify, and distribute this
software and its documentation for any purpose, provided that the
above copyright notice and the following two paragraphs appear in
all copies of this software.
IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
```
# [libjpeg](http://www.ijg.org/)
```
@ -884,6 +925,24 @@ and releases new versions, with the help of Yves Berquin, Andrew
Ellerton, and the tinyXml community.
```
# [ucdn](https://github.com/grigorig/ucdn)
```
Copyright (C) 2012 Grigori Goronzy <greg@kinoho.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
```
# [Wintab API](http://www.wacomeng.com/windows/docs/WintabBackground.htm)
```

2
laf

@ -1 +1 @@
Subproject commit 2630b895fa15b1ff863d47d4ca0ad12e93c22fa9
Subproject commit f232eac37ef0c8a0f5324ed3154b314c64b9eb13

View File

@ -517,7 +517,8 @@ target_link_libraries(app-lib
${PNG_LIBRARIES}
${WEBP_LIBRARIES}
${ZLIB_LIBRARIES}
${FREETYPE_LIBRARIES})
${FREETYPE_LIBRARIES}
${HARFBUZZ_LIBRARIES})
if(ENABLE_SCRIPTING)
target_link_libraries(app-lib script-lib)

View File

@ -997,16 +997,10 @@ public:
bool caretDrawn() const { return m_caretDrawn; }
const gfx::Rect& textBounds() const { return m_textBounds; }
void preProcessChar(const base::utf8_const_iterator& it,
const base::utf8_const_iterator& end,
int& chr,
void preProcessChar(const int index,
const int codepoint,
gfx::Color& fg,
gfx::Color& bg,
bool& drawChar,
bool& moveCaret) override {
if (m_widget->isPassword())
chr = '*';
gfx::Color& bg) override {
// Normal text
auto& colors = SkinTheme::instance()->colors;
bg = ColorNone;
@ -1028,8 +1022,6 @@ public:
fg = colors.disabled();
}
drawChar = true;
moveCaret = true;
m_bg = bg;
}

View File

@ -100,7 +100,7 @@ class StatusBar::Indicators : public HBox {
Graphics* g = ev.graphics();
g->fillRect(bgColor(), rc);
if (textLength() > 0) {
if (!text().empty()) {
g->drawText(text(), textColor, ColorNone,
Point(rc.x, rc.y + rc.h/2 - font()->height()/2));
}

View File

@ -16,7 +16,9 @@
#include "doc/color.h"
#include "doc/image.h"
#include "doc/primitives.h"
#include "ft/algorithm.h"
#include "ft/face.h"
#include "ft/hb_shaper.h"
#include "ft/lib.h"
#include <stdexcept>
@ -46,52 +48,51 @@ doc::Image* render_text(const std::string& fontfile, int fontsize,
doc::clear_image(image, 0);
ft::ForEachGlyph<ft::Face> feg(face);
auto it = base::utf8_const_iterator(text.begin());
auto end = base::utf8_const_iterator(text.end());
while (it != end) {
feg.processChar(
*it,
[&bounds, &image, color, antialias](const ft::Glyph& glyph) {
int t, yimg = - bounds.y + int(glyph.y);
if (feg.initialize(base::utf8_const_iterator(text.begin()),
base::utf8_const_iterator(text.end()))) {
do {
auto glyph = feg.glyph();
if (!glyph)
continue;
for (int v=0; v<int(glyph.bitmap->rows); ++v, ++yimg) {
const uint8_t* p = glyph.bitmap->buffer + v*glyph.bitmap->pitch;
int ximg = - bounds.x + int(glyph.x);
int bit = 0;
int t, yimg = - bounds.y + int(glyph->y);
for (int u=0; u<int(glyph.bitmap->width); ++u, ++ximg) {
int alpha;
for (int v=0; v<int(glyph->bitmap->rows); ++v, ++yimg) {
const uint8_t* p = glyph->bitmap->buffer + v*glyph->bitmap->pitch;
int ximg = - bounds.x + int(glyph->x);
int bit = 0;
if (antialias) {
alpha = *(p++);
}
else {
alpha = ((*p) & (1 << (7 - (bit++))) ? 255: 0);
if (bit == 8) {
bit = 0;
++p;
}
}
for (int u=0; u<int(glyph->bitmap->width); ++u, ++ximg) {
int alpha;
int output_alpha = MUL_UN8(doc::rgba_geta(color), alpha, t);
if (output_alpha) {
doc::color_t output_color =
doc::rgba(doc::rgba_getr(color),
doc::rgba_getg(color),
doc::rgba_getb(color),
output_alpha);
doc::put_pixel(
image, ximg, yimg,
doc::rgba_blender_normal(
doc::get_pixel(image, ximg, yimg),
output_color));
if (antialias) {
alpha = *(p++);
}
else {
alpha = ((*p) & (1 << (7 - (bit++))) ? 255: 0);
if (bit == 8) {
bit = 0;
++p;
}
}
}
});
++it;
int output_alpha = MUL_UN8(doc::rgba_geta(color), alpha, t);
if (output_alpha) {
doc::color_t output_color =
doc::rgba(doc::rgba_getr(color),
doc::rgba_getg(color),
doc::rgba_getb(color),
output_alpha);
doc::put_pixel(
image, ximg, yimg,
doc::rgba_blender_normal(
doc::get_pixel(image, ximg, yimg),
output_color));
}
}
}
} while (feg.nextChar());
}
}
else {

View File

@ -1,4 +1,4 @@
Copyright (c) 2016 David Capello
Copyright (c) 2016-2017 David Capello
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@ -1,4 +1,4 @@
# Aseprite FreeType Wrapper
*Copyright (C) 2016 David Capello*
*Copyright (C) 2016-2017 David Capello*
> Distributed under [MIT license](LICENSE.txt)

175
src/ft/algorithm.h Normal file
View File

@ -0,0 +1,175 @@
// Aseprite FreeType Wrapper
// Copyright (c) 2016-2017 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef FT_ALGORITHM_H_INCLUDED
#define FT_ALGORITHM_H_INCLUDED
#pragma once
#include "base/string.h"
#include "ft/freetype_headers.h"
#include "ft/hb_shaper.h"
#include "gfx/rect.h"
namespace ft {
template<typename FaceFT>
class DefaultShaper {
public:
typedef typename FaceFT::Glyph Glyph;
DefaultShaper(FaceFT& face) : m_face(face) {
}
bool initialize(const base::utf8_const_iterator& it,
const base::utf8_const_iterator& end) {
m_begin = m_it = it;
m_end = end;
return (m_it != end);
}
bool nextChar() {
++m_it;
return (m_it != m_end);
}
int unicodeChar() const {
return *m_it;
}
int charIndex() {
return m_it - m_begin;
}
unsigned int glyphIndex() {
return m_face.cache().getGlyphIndex(m_face, unicodeChar());
}
void glyphOffsetXY(Glyph* glyph) {
// Do nothing
}
void glyphAdvanceXY(const Glyph* glyph, double& x, double& y) {
x += glyph->ft_glyph->advance.x / double(1 << 16);
y += glyph->ft_glyph->advance.y / double(1 << 16);
}
private:
FaceFT& m_face;
base::utf8_const_iterator m_begin, m_end, m_it;
};
template<typename FaceFT,
typename Shaper = HBShaper<FaceFT> >
class ForEachGlyph {
public:
typedef typename FaceFT::Glyph Glyph;
ForEachGlyph(FaceFT& face)
: m_face(face)
, m_shaper(face)
, m_glyph(nullptr)
, m_useKerning(FT_HAS_KERNING((FT_Face)face) ? true: false)
, m_prevGlyph(0)
, m_x(0.0), m_y(0.0) {
}
~ForEachGlyph() {
unloadGlyph();
}
int unicodeChar() { return m_shaper.unicodeChar(); }
int charIndex() { return m_shaper.charIndex(); }
const Glyph* glyph() const { return m_glyph; }
bool initialize(const base::utf8_const_iterator& it,
const base::utf8_const_iterator& end) {
bool res = m_shaper.initialize(it, end);
if (res)
prepareGlyph();
return res;
}
bool nextChar() {
m_prevGlyph = m_shaper.glyphIndex();
if (m_shaper.nextChar()) {
prepareGlyph();
return true;
}
else
return false;
}
private:
void prepareGlyph() {
FT_UInt glyphIndex = m_shaper.glyphIndex();
double initialX = m_x;
if (m_useKerning && m_prevGlyph && glyphIndex) {
FT_Vector kerning;
FT_Get_Kerning(m_face, m_prevGlyph, glyphIndex,
FT_KERNING_DEFAULT, &kerning);
m_x += kerning.x / 64.0;
}
unloadGlyph();
// Load new glyph
m_glyph = m_face.cache().loadGlyph(m_face, glyphIndex, m_face.antialias());
if (m_glyph) {
m_glyph->bitmap = &FT_BitmapGlyph(m_glyph->ft_glyph)->bitmap;
m_glyph->x = m_x
+ m_glyph->bearingX;
m_glyph->y = m_y
+ m_face.height()
+ m_face.descender() // descender is negative
- m_glyph->bearingY;
m_shaper.glyphOffsetXY(m_glyph);
m_shaper.glyphAdvanceXY(m_glyph, m_x, m_y);
m_glyph->startX = initialX;
m_glyph->endX = m_x;
}
}
void unloadGlyph() {
if (m_glyph) {
m_face.cache().doneGlyph(m_glyph);
m_glyph = nullptr;
}
}
private:
FaceFT& m_face;
Shaper m_shaper;
Glyph* m_glyph;
bool m_useKerning;
FT_UInt m_prevGlyph;
double m_x, m_y;
};
template<typename FaceFT>
gfx::Rect calc_text_bounds(FaceFT& face, const std::string& str) {
gfx::Rect bounds(0, 0, 0, 0);
ForEachGlyph<FaceFT> feg(face);
if (feg.initialize(base::utf8_const_iterator(str.begin()),
base::utf8_const_iterator(str.end()))) {
do {
if (auto glyph = feg.glyph())
bounds |= gfx::Rect(int(glyph->x),
int(glyph->y),
glyph->bitmap->width,
glyph->bitmap->rows);
} while (feg.nextChar());
}
return bounds;
}
} // namespace ft
#endif

View File

@ -10,9 +10,7 @@
#include "base/debug.h"
#include "base/disable_copying.h"
#include "base/string.h"
#include "ft/freetype_headers.h"
#include "gfx/rect.h"
#include <map>
@ -33,6 +31,8 @@ namespace ft {
template<typename Cache>
class FaceFT {
public:
typedef ft::Glyph Glyph;
FaceFT(FT_Face face) : m_face(face) {
}
@ -94,79 +94,6 @@ namespace ft {
DISABLE_COPYING(FaceFT);
};
template<typename FaceFT>
class ForEachGlyph {
public:
ForEachGlyph(FaceFT& face)
: m_face(face)
, m_useKerning(FT_HAS_KERNING((FT_Face)face) ? true: false)
, m_prevGlyph(0)
, m_x(0.0), m_y(0.0)
{
}
template<typename ProcessGlyph>
void processChar(const int chr, ProcessGlyph processGlyph) {
FT_UInt glyphIndex = m_face.cache().getGlyphIndex(m_face, chr);
double initialX = m_x;
if (m_useKerning && m_prevGlyph && glyphIndex) {
FT_Vector kerning;
FT_Get_Kerning(m_face, m_prevGlyph, glyphIndex,
FT_KERNING_DEFAULT, &kerning);
m_x += kerning.x / 64.0;
}
Glyph* glyph = m_face.cache().loadGlyph(
m_face, glyphIndex, m_face.antialias());
if (glyph) {
glyph->bitmap = &FT_BitmapGlyph(glyph->ft_glyph)->bitmap;
glyph->x = m_x + glyph->bearingX;
glyph->y = m_y
+ m_face.height()
+ m_face.descender() // descender is negative
- glyph->bearingY;
m_x += glyph->ft_glyph->advance.x / double(1 << 16);
m_y += glyph->ft_glyph->advance.y / double(1 << 16);
glyph->startX = initialX;
glyph->endX = m_x;
processGlyph(*glyph);
m_face.cache().doneGlyph(glyph);
}
m_prevGlyph = glyphIndex;
}
private:
FaceFT& m_face;
bool m_useKerning;
FT_UInt m_prevGlyph;
double m_x, m_y;
};
template<typename FaceFT>
gfx::Rect calc_text_bounds(FaceFT& face, const std::string& str) {
gfx::Rect bounds(0, 0, 0, 0);
auto it = base::utf8_const_iterator(str.begin());
auto end = base::utf8_const_iterator(str.end());
ForEachGlyph<FaceFT> feg(face);
for (; it != end; ++it) {
feg.processChar(
*it,
[&bounds](Glyph& glyph) {
bounds |= gfx::Rect(int(glyph.x),
int(glyph.y),
glyph.bitmap->width,
glyph.bitmap->rows);
});
}
return bounds;
}
class NoCache {
public:
void invalidate() {
@ -261,8 +188,6 @@ namespace ft {
std::map<FT_UInt, Glyph*> m_glyphMap;
};
typedef FaceFT<SimpleCache> Face;
} // namespace ft
#endif

44
src/ft/hb_face.h Normal file
View File

@ -0,0 +1,44 @@
// Aseprite FreeType Wrapper
// Copyright (c) 2017 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef FT_HB_FACE_H_INCLUDED
#define FT_HB_FACE_H_INCLUDED
#pragma once
#include "base/string.h"
#include "ft/face.h"
#include <hb.h>
#include <hb-ft.h>
namespace ft {
template<typename FaceFT>
class HBFace : public FaceFT {
public:
HBFace(FT_Face face) : FaceFT(face) {
m_font = hb_ft_font_create((FT_Face)face, nullptr);
m_buffer = hb_buffer_create();
}
~HBFace() {
if (m_buffer) hb_buffer_destroy(m_buffer);
if (m_font) hb_font_destroy(m_font);
}
hb_font_t* font() const { return m_font; }
hb_buffer_t* buffer() const { return m_buffer; }
private:
hb_buffer_t* m_buffer;
hb_font_t* m_font;
};
typedef HBFace<FaceFT<SimpleCache> > Face;
} // namespace ft
#endif

101
src/ft/hb_shaper.h Normal file
View File

@ -0,0 +1,101 @@
// Aseprite FreeType Wrapper
// Copyright (c) 2017 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef FT_HB_SHAPER_H_INCLUDED
#define FT_HB_SHAPER_H_INCLUDED
#pragma once
#include "ft/hb_face.h"
#include <vector>
namespace ft {
template<typename HBFace>
class HBShaper {
public:
HBShaper(HBFace& face)
: m_face(face)
, m_unicodeFuncs(hb_buffer_get_unicode_funcs(face.buffer())) {
}
bool initialize(const base::utf8_const_iterator& it,
const base::utf8_const_iterator& end) {
m_begin = m_it = it;
m_end = end;
m_index = 0;
hb_buffer_t* buf = m_face.buffer();
hb_buffer_reset(buf);
for (auto it=m_it; it!=end; ++it)
hb_buffer_add(buf, *it, 0);
hb_buffer_set_content_type(buf, HB_BUFFER_CONTENT_TYPE_UNICODE);
hb_buffer_set_direction(buf, HB_DIRECTION_LTR);
hb_buffer_guess_segment_properties(buf);
hb_shape(m_face.font(), buf, nullptr, 0);
m_glyphInfo = hb_buffer_get_glyph_infos(buf, &m_glyphCount);
m_glyphPos = hb_buffer_get_glyph_positions(buf, &m_glyphCount);
return (m_glyphCount > 0);
}
bool nextChar() {
advanceIterator(m_it);
++m_index;
return (m_index < m_glyphCount);
}
int unicodeChar() const {
auto it = m_it;
return advanceIterator(it);
}
int charIndex() {
return m_it - m_begin;
}
unsigned int glyphIndex() const {
return m_glyphInfo[m_index].codepoint;
}
void glyphOffsetXY(Glyph* glyph) {
glyph->x += m_glyphPos[m_index].x_offset / 64.0;
glyph->y += m_glyphPos[m_index].y_offset / 64.0;
}
void glyphAdvanceXY(const Glyph* glyph, double& x, double& y) {
x += m_glyphPos[m_index].x_advance / 64.0;
y += m_glyphPos[m_index].y_advance / 64.0;
}
private:
hb_codepoint_t advanceIterator(base::utf8_const_iterator& it) const {
hb_codepoint_t chr = *it;
hb_codepoint_t newChr = 0;
++it;
while (it != m_end) {
if (!hb_unicode_compose(m_unicodeFuncs, chr, *it, &newChr))
break;
chr = newChr;
++it;
}
return chr;
}
HBFace& m_face;
hb_glyph_info_t* m_glyphInfo;
hb_glyph_position_t* m_glyphPos;
unsigned int m_glyphCount;
int m_index;
base::utf8_const_iterator m_begin, m_end, m_it;
hb_unicode_funcs_t* m_unicodeFuncs;
};
} // namespace ft
#endif

View File

@ -11,6 +11,7 @@
#include "she/common/freetype_font.h"
#include "base/string.h"
#include "ft/algorithm.h"
#include "gfx/point.h"
#include "gfx/size.h"

View File

@ -8,7 +8,7 @@
#define SHE_COMMON_FREETYPE_FONT_H_INCLUDED
#pragma once
#include "ft/face.h"
#include "ft/hb_face.h"
#include "ft/lib.h"
#include "she/font.h"
@ -17,6 +17,8 @@ namespace she {
class FreeTypeFont : public Font {
public:
typedef ft::Face Face;
FreeTypeFont(const char* filename, int height);
~FreeTypeFont();
@ -29,11 +31,11 @@ namespace she {
void setSize(int size) override;
void setAntialias(bool antialias) override;
ft::Face& face() { return m_face; }
Face& face() { return m_face; }
private:
mutable ft::Lib m_ft;
mutable ft::Face m_face;
mutable Face m_face;
};
FreeTypeFont* loadFreeTypeFont(const char* filename, int height);

View File

@ -10,6 +10,8 @@
#include "she/draw_text.h"
#include "ft/algorithm.h"
#include "ft/hb_shaper.h"
#include "gfx/clip.h"
#include "she/common/freetype_font.h"
#include "she/common/generic_surface.h"
@ -18,15 +20,14 @@
namespace she {
gfx::Rect draw_text(Surface* surface, Font* font,
base::utf8_const_iterator it,
const base::utf8_const_iterator& begin,
const base::utf8_const_iterator& end,
gfx::Color fg, gfx::Color bg,
int x, int y,
DrawTextDelegate* delegate)
{
base::utf8_const_iterator it = begin;
gfx::Rect textBounds;
bool drawChar = true;
bool moveCaret = true;
switch (font->type()) {
@ -34,30 +35,29 @@ gfx::Rect draw_text(Surface* surface, Font* font,
SpriteSheetFont* ssFont = static_cast<SpriteSheetFont*>(font);
while (it != end) {
int chr = *it;
if (delegate)
delegate->preProcessChar(it, end, chr, fg, bg, drawChar, moveCaret);
if (moveCaret) {
gfx::Rect charBounds = ssFont->getCharBounds(chr);
gfx::Rect outCharBounds(x, y, charBounds.w, charBounds.h);
if (delegate && !delegate->preDrawChar(outCharBounds))
break;
if (!charBounds.isEmpty()) {
if (surface && drawChar) {
Surface* sheet = ssFont->getSurfaceSheet();
SurfaceLock lock(sheet);
surface->drawColoredRgbaSurface(sheet, fg, bg, gfx::Clip(x, y, charBounds));
}
}
textBounds |= outCharBounds;
if (delegate)
delegate->postDrawChar(outCharBounds);
x += charBounds.w;
if (delegate) {
int i = it-begin;
delegate->preProcessChar(i, chr, fg, bg);
}
gfx::Rect charBounds = ssFont->getCharBounds(chr);
gfx::Rect outCharBounds(x, y, charBounds.w, charBounds.h);
if (delegate && !delegate->preDrawChar(outCharBounds))
break;
if (!charBounds.isEmpty()) {
if (surface) {
Surface* sheet = ssFont->getSurfaceSheet();
SurfaceLock lock(sheet);
surface->drawColoredRgbaSurface(sheet, fg, bg, gfx::Clip(x, y, charBounds));
}
}
textBounds |= outCharBounds;
if (delegate)
delegate->postDrawChar(outCharBounds);
x += charBounds.w;
++it;
}
break;
@ -66,7 +66,6 @@ gfx::Rect draw_text(Surface* surface, Font* font,
case FontType::kTrueType: {
FreeTypeFont* ttFont = static_cast<FreeTypeFont*>(font);
bool antialias = ttFont->face().antialias();
bool done = false;
int fg_alpha = gfx::geta(fg);
gfx::Rect clipBounds;
@ -76,119 +75,109 @@ gfx::Rect draw_text(Surface* surface, Font* font,
surface->getFormat(&fd);
}
ft::ForEachGlyph<ft::Face> feg(ttFont->face());
while (it != end) {
int chr = *it;
if (delegate)
delegate->preProcessChar(it, end, chr, fg, bg, drawChar, moveCaret);
ft::ForEachGlyph<FreeTypeFont::Face> feg(ttFont->face());
if (feg.initialize(it, end)) {
do {
if (delegate) {
delegate->preProcessChar(feg.charIndex(),
feg.unicodeChar(), fg, bg);
}
if (!moveCaret) {
++it;
continue;
}
auto glyph = feg.glyph();
if (!glyph)
continue;
feg.processChar(
chr,
[x, y, fg, fg_alpha, bg, antialias, surface,
&clipBounds, &textBounds, &fd, &done, delegate, drawChar]
(const ft::Glyph& glyph) {
gfx::Rect origDstBounds(
x + int(glyph.startX),
y + int(glyph.y),
int(glyph.endX) - int(glyph.startX),
int(glyph.bitmap->rows) ? int(glyph.bitmap->rows): 1);
gfx::Rect origDstBounds(
x + int(glyph->startX),
y + int(glyph->y),
int(glyph->endX) - int(glyph->startX),
int(glyph->bitmap->rows) ? int(glyph->bitmap->rows): 1);
if (delegate && !delegate->preDrawChar(origDstBounds)) {
done = true;
return;
}
origDstBounds.x = x + int(glyph.x);
origDstBounds.w = int(glyph.bitmap->width);
origDstBounds.h = int(glyph.bitmap->rows);
if (delegate && !delegate->preDrawChar(origDstBounds))
break;
gfx::Rect dstBounds = origDstBounds;
if (surface)
dstBounds &= clipBounds;
origDstBounds.x = x + int(glyph->x);
origDstBounds.w = int(glyph->bitmap->width);
origDstBounds.h = int(glyph->bitmap->rows);
if (surface && drawChar && !dstBounds.isEmpty()) {
int clippedRows = dstBounds.y - origDstBounds.y;
int dst_y = dstBounds.y;
int t;
for (int v=0; v<dstBounds.h; ++v, ++dst_y) {
int bit = 0;
const uint8_t* p = glyph.bitmap->buffer
+ (v+clippedRows)*glyph.bitmap->pitch;
int dst_x = dstBounds.x;
uint32_t* dst_address =
(uint32_t*)surface->getData(dst_x, dst_y);
gfx::Rect dstBounds = origDstBounds;
if (surface)
dstBounds &= clipBounds;
// Skip first clipped pixels
for (int u=0; u<dstBounds.x-origDstBounds.x; ++u) {
if (antialias) {
if (surface && !dstBounds.isEmpty()) {
int clippedRows = dstBounds.y - origDstBounds.y;
int dst_y = dstBounds.y;
int t;
for (int v=0; v<dstBounds.h; ++v, ++dst_y) {
int bit = 0;
const uint8_t* p = glyph->bitmap->buffer
+ (v+clippedRows)*glyph->bitmap->pitch;
int dst_x = dstBounds.x;
uint32_t* dst_address =
(uint32_t*)surface->getData(dst_x, dst_y);
// Skip first clipped pixels
for (int u=0; u<dstBounds.x-origDstBounds.x; ++u) {
if (antialias) {
++p;
}
else {
if (bit == 8) {
bit = 0;
++p;
}
else {
if (bit == 8) {
bit = 0;
++p;
}
}
}
for (int u=0; u<dstBounds.w; ++u, ++dst_x) {
ASSERT(clipBounds.contains(gfx::Point(dst_x, dst_y)));
int alpha;
if (antialias) {
alpha = *(p++);
}
else {
alpha = ((*p) & (1 << (7 - (bit++))) ? 255: 0);
if (bit == 8) {
bit = 0;
++p;
}
}
uint32_t backdrop = *dst_address;
gfx::Color backdropColor =
gfx::rgba(
((backdrop & fd.redMask) >> fd.redShift),
((backdrop & fd.greenMask) >> fd.greenShift),
((backdrop & fd.blueMask) >> fd.blueShift),
((backdrop & fd.alphaMask) >> fd.alphaShift));
gfx::Color output = gfx::rgba(gfx::getr(fg),
gfx::getg(fg),
gfx::getb(fg),
MUL_UN8(fg_alpha, alpha, t));
if (gfx::geta(bg) > 0)
output = blend(blend(backdropColor, bg), output);
else
output = blend(backdropColor, output);
*dst_address =
((gfx::getr(output) << fd.redShift ) & fd.redMask ) |
((gfx::getg(output) << fd.greenShift) & fd.greenMask) |
((gfx::getb(output) << fd.blueShift ) & fd.blueMask ) |
((gfx::geta(output) << fd.alphaShift) & fd.alphaMask);
++dst_address;
}
}
for (int u=0; u<dstBounds.w; ++u, ++dst_x) {
ASSERT(clipBounds.contains(gfx::Point(dst_x, dst_y)));
int alpha;
if (antialias) {
alpha = *(p++);
}
else {
alpha = ((*p) & (1 << (7 - (bit++))) ? 255: 0);
if (bit == 8) {
bit = 0;
++p;
}
}
uint32_t backdrop = *dst_address;
gfx::Color backdropColor =
gfx::rgba(
((backdrop & fd.redMask) >> fd.redShift),
((backdrop & fd.greenMask) >> fd.greenShift),
((backdrop & fd.blueMask) >> fd.blueShift),
((backdrop & fd.alphaMask) >> fd.alphaShift));
gfx::Color output = gfx::rgba(gfx::getr(fg),
gfx::getg(fg),
gfx::getb(fg),
MUL_UN8(fg_alpha, alpha, t));
if (gfx::geta(bg) > 0)
output = blend(blend(backdropColor, bg), output);
else
output = blend(backdropColor, output);
*dst_address =
((gfx::getr(output) << fd.redShift ) & fd.redMask ) |
((gfx::getg(output) << fd.greenShift) & fd.greenMask) |
((gfx::getb(output) << fd.blueShift ) & fd.blueMask ) |
((gfx::geta(output) << fd.alphaShift) & fd.alphaMask);
++dst_address;
}
}
}
if (!origDstBounds.w) origDstBounds.w = 1;
if (!origDstBounds.h) origDstBounds.h = 1;
textBounds |= origDstBounds;
if (delegate)
delegate->postDrawChar(origDstBounds);
});
if (done)
break;
++it;
if (!origDstBounds.w) origDstBounds.w = 1;
if (!origDstBounds.h) origDstBounds.h = 1;
textBounds |= origDstBounds;
if (delegate)
delegate->postDrawChar(origDstBounds);
} while (feg.nextChar());
}
break;
}

View File

@ -22,18 +22,10 @@ namespace she {
public:
virtual ~DrawTextDelegate() { }
// This is called before drawing the character. Here you can
// modify the final painted character (e.g. for passwords you can
// modify chr='*') and change the specific fg/bg color for this
// char (e.g. to change the color depending if is a
// selected/highlighted portion of text).
virtual void preProcessChar(const base::utf8_const_iterator& it,
const base::utf8_const_iterator& end,
int& chr,
gfx::Color& fg,
gfx::Color& bg,
bool& drawChar,
bool& moveCaret) {
// This is called before drawing the character.
virtual void preProcessChar(const int index,
const int codepoint,
gfx::Color& fg, gfx::Color& bg) {
// Do nothing
}
@ -51,7 +43,7 @@ namespace she {
// (e.g. measure how much space will use the text without drawing
// it).
gfx::Rect draw_text(Surface* surface, Font* font,
base::utf8_const_iterator it,
const base::utf8_const_iterator& begin,
const base::utf8_const_iterator& end,
gfx::Color fg, gfx::Color bg,
int x, int y,

View File

@ -278,7 +278,7 @@ void ComboBox::setSelectedItemIndex(int itemIndex)
ListItem* item = *it;
m_entry->setText(item->text());
if (isEditable())
m_entry->selectText(m_entry->textLength(), m_entry->textLength());
m_entry->setCaretToEnd();
onChange();
}
@ -394,7 +394,7 @@ void ComboBox::onSizeHint(SizeHintEvent& ev)
Size entrySize = m_entry->sizeHint();
Size reqSize = entrySize;
// Get the text-length of every item and put in 'w' the maximum value
// Get the text-length of every item
ListItems::iterator it, end = m_items.end();
for (it = m_items.begin(); it != end; ++it) {
int item_w =
@ -498,7 +498,7 @@ bool ComboBoxEntry::onProcessMessage(Message* msg)
// of the text. We don't select the whole text so the user can
// delete the last caracters using backspace and complete the
// item name.
selectText(textLength(), textLength());
setCaretToEnd();
}
return result;
}

View File

@ -30,25 +30,6 @@
namespace ui {
namespace {
class MeasureTextDelegate : public she::DrawTextDelegate {
public:
MeasureTextDelegate() { }
gfx::Rect bounds() const { return m_bounds; }
bool preDrawChar(const gfx::Rect& charBounds) override {
m_bounds |= charBounds;
return true;
}
private:
gfx::Rect m_bounds;
};
}
Entry::Entry(const std::size_t maxsize, const char* format, ...)
: Widget(kEntryWidget)
, m_timer(500, this)
@ -59,7 +40,6 @@ Entry::Entry(const std::size_t maxsize, const char* format, ...)
, m_hidden(false)
, m_state(false)
, m_readonly(false)
, m_password(false)
, m_recent_focused(false)
, m_lock_selection(false)
, m_translate_dead_keys(true)
@ -67,7 +47,7 @@ Entry::Entry(const std::size_t maxsize, const char* format, ...)
enableFlags(CTRL_RIGHT_CLICK);
// formatted string
char buf[4096];
char buf[4096]; // TODO buffer overflow
if (format) {
va_list ap;
va_start(ap, format);
@ -101,21 +81,11 @@ bool Entry::isReadOnly() const
return m_readonly;
}
bool Entry::isPassword() const
{
return m_password;
}
void Entry::setReadOnly(bool state)
{
m_readonly = state;
}
void Entry::setPassword(bool state)
{
m_password = state;
}
void Entry::showCaret()
{
m_hidden = false;
@ -128,10 +98,15 @@ void Entry::hideCaret()
invalidate();
}
int Entry::lastCaretPos() const
{
return int(m_boxes.size()-1);
}
void Entry::setCaretPos(int pos)
{
gfx::Size caretSize = theme()->getEntryCaretSize(this);
int textlen = base::utf8_length(text());
int textlen = lastCaretPos();
m_caret = MID(0, pos, textlen);
m_scroll = MID(0, m_scroll, textlen);
@ -140,21 +115,17 @@ void Entry::setCaretPos(int pos)
m_scroll = m_caret;
// Forward scroll
else if (m_caret > m_scroll) {
auto it = base::utf8_const_iterator(text().begin()) + m_scroll;
int xLimit = bounds().x2() - border().right();
while (m_caret > m_scroll) {
MeasureTextDelegate delegate;
she::draw_text(nullptr, font(), it, it+(m_caret-m_scroll),
gfx::ColorNone, gfx::ColorNone, 0, 0,
&delegate);
int segmentWidth = 0;
for (int j=m_scroll; j<m_caret; ++j)
segmentWidth += m_boxes[j].width;
int x = bounds().x + border().left()
+ delegate.bounds().w + caretSize.w;
if (x < bounds().x2() - border().right())
int x = bounds().x + border().left() + segmentWidth + caretSize.w;
if (x < xLimit)
break;
else {
++it;
else
++m_scroll;
}
}
}
@ -164,9 +135,15 @@ void Entry::setCaretPos(int pos)
invalidate();
}
void Entry::setCaretToEnd()
{
int end = lastCaretPos();
selectText(end, end);
}
void Entry::selectText(int from, int to)
{
int end = base::utf8_length(text());
int end = lastCaretPos();
m_select = from;
setCaretPos(from); // to move scroll
@ -186,6 +163,21 @@ void Entry::deselectText()
invalidate();
}
std::string Entry::selectedText() const
{
int selbeg, selend;
getEntryThemeInfo(nullptr, nullptr, nullptr, &selbeg, &selend);
if (selbeg >= 0 && selend >= 0) {
ASSERT(selbeg < int(m_boxes.size()));
ASSERT(selend < int(m_boxes.size()));
return text().substr(m_boxes[selbeg].from,
m_boxes[selend].to - m_boxes[selbeg].from);
}
else
return std::string();
}
void Entry::setSuffix(const std::string& suffix)
{
m_suffix = suffix;
@ -198,7 +190,7 @@ void Entry::setTranslateDeadKeys(bool state)
}
void Entry::getEntryThemeInfo(int* scroll, int* caret, int* state,
int* selbeg, int* selend)
int* selbeg, int* selend) const
{
if (scroll) *scroll = m_scroll;
if (caret) *caret = m_caret;
@ -349,7 +341,7 @@ bool Entry::onProcessMessage(Message* msg)
// Select dead-key
if (keymsg->isDeadKey()) {
if (base::from_utf8(text()).size() < m_maxsize)
if (lastCaretPos() < m_maxsize)
selectText(m_caret-1, m_caret);
}
return true;
@ -463,8 +455,9 @@ void Entry::onPaint(PaintEvent& ev)
void Entry::onSetText()
{
Widget::onSetText();
recalcCharBoxes(text());
int textlen = textLength();
int textlen = lastCaretPos();
if (m_caret >= 0 && m_caret > textlen)
m_caret = textlen;
}
@ -492,16 +485,14 @@ int Entry::getCaretFromMouse(MouseMessage* mousemsg)
return MAX(0, m_scroll-1);
}
int textlen = base::utf8_length(text());
auto it = base::utf8_const_iterator(text().begin()) + m_scroll;
int i = MIN(m_scroll, textlen);
for (; i<textlen; ++i) {
MeasureTextDelegate delegate;
she::draw_text(nullptr, font(), it, it+(i-m_scroll),
gfx::ColorNone, gfx::ColorNone, 0, 0,
&delegate);
int lastPos = lastCaretPos();
int i = MIN(m_scroll, lastPos);
for (; i<lastPos; ++i) {
int segmentWidth = 0;
for (int j=m_scroll; j<i; ++j)
segmentWidth += m_boxes[j].width;
int x = bounds().x + border().left() + delegate.bounds().w;
int x = bounds().x + border().left() + segmentWidth;
if (mouseX > bounds().x2() - border().right()) {
if (x >= bounds().x2() - border().right()) {
@ -519,12 +510,12 @@ int Entry::getCaretFromMouse(MouseMessage* mousemsg)
}
}
return MID(0, i, textlen);
return MID(0, i, lastPos);
}
void Entry::executeCmd(EntryCmd cmd, int unicodeChar, bool shift_pressed)
{
std::wstring text = base::from_utf8(this->text());
std::string text = this->text();
int c, selbeg, selend;
getEntryThemeInfo(NULL, NULL, NULL, &selbeg, &selend);
@ -537,7 +528,8 @@ void Entry::executeCmd(EntryCmd cmd, int unicodeChar, bool shift_pressed)
case EntryCmd::InsertChar:
// delete the entire selection
if (selbeg >= 0) {
text.erase(selbeg, selend-selbeg+1);
text.erase(m_boxes[selbeg].from,
m_boxes[selend].to - m_boxes[selbeg].from);
m_caret = selbeg;
@ -549,13 +541,20 @@ void Entry::executeCmd(EntryCmd cmd, int unicodeChar, bool shift_pressed)
// we need to make m_scroll=0 to show the new inserted char.)
// In this way, we first ensure a m_scroll value enough to
// show the new inserted character.
recalcCharBoxes(text);
setCaretPos(m_caret);
}
// put the character
if (text.size() < m_maxsize) {
ASSERT((std::size_t)m_caret <= text.size());
text.insert(m_caret++, 1, unicodeChar);
// Convert the unicode character -> wstring -> utf-8 string -> insert the utf-8 string
if (lastCaretPos() < m_maxsize) {
ASSERT((std::size_t)m_caret <= lastCaretPos());
std::wstring unicodeStr;
unicodeStr.push_back(unicodeChar);
text.insert(m_boxes[m_caret].from,
base::to_utf8(unicodeStr));
++m_caret;
}
m_select = -1;
@ -630,20 +629,20 @@ void Entry::executeCmd(EntryCmd cmd, int unicodeChar, bool shift_pressed)
// delete the entire selection
if (selbeg >= 0) {
// *cut* text!
if (cmd == EntryCmd::Cut) {
std::wstring selected = text.substr(selbeg, selend - selbeg + 1);
clip::set_text(base::to_utf8(selected));
}
if (cmd == EntryCmd::Cut)
clip::set_text(selectedText());
// remove text
text.erase(selbeg, selend-selbeg+1);
text.erase(m_boxes[selbeg].from,
m_boxes[selend].to - m_boxes[selbeg].from);
m_caret = selbeg;
}
// delete the next character
else {
if (m_caret < (int)text.size())
text.erase(m_caret, 1);
text.erase(m_boxes[m_caret].from,
m_boxes[m_caret].to - m_boxes[m_caret].from);
}
m_select = -1;
@ -654,44 +653,53 @@ void Entry::executeCmd(EntryCmd cmd, int unicodeChar, bool shift_pressed)
if (clip::get_text(clipboard)) {
// delete the entire selection
if (selbeg >= 0) {
text.erase(selbeg, selend-selbeg+1);
text.erase(m_boxes[selbeg].from,
m_boxes[selend].to - m_boxes[selbeg].from);
m_caret = selbeg;
m_select = -1;
}
// paste text
for (c=0; c<base::utf8_length(clipboard); c++) {
if (text.size() < m_maxsize)
text.insert(m_caret+c, 1,
*(base::utf8_const_iterator(clipboard.begin())+c));
else
break;
// Paste text
recalcCharBoxes(text);
int oldBoxes = m_boxes.size();
text.insert(m_boxes[m_caret].from, clipboard);
// Remove extra chars that do not fit in m_maxsize
recalcCharBoxes(text);
if (lastCaretPos() > m_maxsize) {
text.erase(m_boxes[m_maxsize].from,
text.size() - m_boxes[m_maxsize].from);
recalcCharBoxes(text);
}
setCaretPos(m_caret+c);
int newBoxes = m_boxes.size();
setCaretPos(m_caret+(newBoxes - oldBoxes));
}
break;
}
case EntryCmd::Copy:
if (selbeg >= 0) {
std::wstring selected = text.substr(selbeg, selend - selbeg + 1);
clip::set_text(base::to_utf8(selected));
}
if (selbeg >= 0)
clip::set_text(selectedText());
break;
case EntryCmd::DeleteBackward:
// delete the entire selection
if (selbeg >= 0) {
text.erase(selbeg, selend-selbeg+1);
text.erase(m_boxes[selbeg].from,
m_boxes[selend].to - m_boxes[selbeg].from);
m_caret = selbeg;
}
// delete the previous character
else {
if (m_caret > 0)
text.erase(--m_caret, 1);
if (m_caret > 0) {
--m_caret;
text.erase(m_boxes[m_caret].from,
m_boxes[m_caret].to - m_boxes[m_caret].from);
}
}
m_select = -1;
@ -700,13 +708,15 @@ void Entry::executeCmd(EntryCmd cmd, int unicodeChar, bool shift_pressed)
case EntryCmd::DeleteBackwardWord:
m_select = m_caret;
backwardWord();
if (m_caret < m_select)
text.erase(m_caret, m_select-m_caret);
if (m_caret < m_select) {
text.erase(m_boxes[m_caret].from,
m_boxes[m_select].to - m_boxes[m_caret].from);
}
m_select = -1;
break;
case EntryCmd::DeleteForwardToEndOfLine:
text.erase(m_caret);
text.erase(m_boxes[m_caret].from);
break;
case EntryCmd::SelectAll:
@ -714,9 +724,8 @@ void Entry::executeCmd(EntryCmd cmd, int unicodeChar, bool shift_pressed)
break;
}
std::string newText = base::to_utf8(text);
if (newText != this->text()) {
setText(newText.c_str());
if (text != this->text()) {
setText(text);
onChange();
}
@ -730,18 +739,17 @@ void Entry::executeCmd(EntryCmd cmd, int unicodeChar, bool shift_pressed)
void Entry::forwardWord()
{
base::utf8_const_iterator utf8_begin = base::utf8_const_iterator(text().begin());
int textlen = base::utf8_length(text());
int textlen = lastCaretPos();
int ch;
for (; m_caret < textlen; m_caret++) {
ch = *(utf8_begin + m_caret);
for (; m_caret < textlen; ++m_caret) {
ch = m_boxes[m_caret].codepoint;
if (IS_WORD_CHAR(ch))
break;
}
for (; m_caret < textlen; m_caret++) {
ch = *(utf8_begin + m_caret);
for (; m_caret < textlen; ++m_caret) {
ch = m_boxes[m_caret].codepoint;
if (!IS_WORD_CHAR(ch)) {
++m_caret;
break;
@ -751,17 +759,16 @@ void Entry::forwardWord()
void Entry::backwardWord()
{
base::utf8_const_iterator utf8_begin = base::utf8_const_iterator(text().begin());
int ch;
for (--m_caret; m_caret >= 0; --m_caret) {
ch = *(utf8_begin + m_caret);
ch = m_boxes[m_caret].codepoint;
if (IS_WORD_CHAR(ch))
break;
}
for (; m_caret >= 0; --m_caret) {
ch = *(utf8_begin + m_caret);
ch = m_boxes[m_caret].codepoint;
if (!IS_WORD_CHAR(ch)) {
++m_caret;
break;
@ -774,7 +781,8 @@ void Entry::backwardWord()
bool Entry::isPosInSelection(int pos)
{
return (pos >= MIN(m_caret, m_select) && pos <= MAX(m_caret, m_select));
return (pos >= MIN(m_caret, m_select) &&
pos <= MAX(m_caret, m_select));
}
void Entry::showEditPopupMenu(const gfx::Point& pt)
@ -798,4 +806,55 @@ void Entry::showEditPopupMenu(const gfx::Point& pt)
menu.showPopup(pt);
}
class Entry::CalcBoxesTextDelegate : public she::DrawTextDelegate {
public:
CalcBoxesTextDelegate(const int end) : m_end(end) {
}
const Entry::CharBoxes& boxes() const { return m_boxes; }
void preProcessChar(const int index,
const int codepoint,
gfx::Color& fg, gfx::Color& bg) override {
if (!m_boxes.empty())
m_boxes.back().to = index;
m_box = CharBox();
m_box.codepoint = codepoint;
m_box.from = index;
m_box.to = m_end;
}
bool preDrawChar(const gfx::Rect& charBounds) override {
m_box.width = charBounds.w;
return true;
}
void postDrawChar(const gfx::Rect& charBounds) override {
m_boxes.push_back(m_box);
}
private:
Entry::CharBox m_box;
Entry::CharBoxes m_boxes;
int m_end;
};
void Entry::recalcCharBoxes(const std::string& text)
{
int lastTextIndex = int(text.size());
CalcBoxesTextDelegate delegate(lastTextIndex);
she::draw_text(nullptr, font(),
base::utf8_const_iterator(text.begin()),
base::utf8_const_iterator(text.end()),
gfx::ColorNone, gfx::ColorNone, 0, 0, &delegate);
m_boxes = delegate.boxes();
// A last box for the last position
CharBox box;
box.codepoint = 0;
box.from = box.to = lastTextIndex;
m_boxes.push_back(box);
}
} // namespace ui

View File

@ -23,18 +23,21 @@ namespace ui {
void setMaxTextLength(const std::size_t maxsize);
bool isPassword() const;
bool isReadOnly() const;
void setReadOnly(bool state);
void setPassword(bool state);
void showCaret();
void hideCaret();
int caretPos() const { return m_caret; }
int lastCaretPos() const;
void setCaretPos(int pos);
void setCaretToEnd();
void selectText(int from, int to);
void selectAllText();
void deselectText();
std::string selectedText() const;
void setSuffix(const std::string& suffix);
const std::string& getSuffix() { return m_suffix; }
@ -43,7 +46,7 @@ namespace ui {
// for themes
void getEntryThemeInfo(int* scroll, int* caret, int* state,
int* selbeg, int* selend);
int* selbeg, int* selend) const;
gfx::Rect getEntryTextBounds() const;
// Signals
@ -86,7 +89,20 @@ namespace ui {
void backwardWord();
bool isPosInSelection(int pos);
void showEditPopupMenu(const gfx::Point& pt);
void recalcCharBoxes(const std::string& text);
class CalcBoxesTextDelegate;
struct CharBox {
int codepoint;
int from, to;
int width;
CharBox() { codepoint = from = to = width = 0; }
};
typedef std::vector<CharBox> CharBoxes;
CharBoxes m_boxes;
Timer m_timer;
std::size_t m_maxsize;
int m_caret;
@ -95,7 +111,6 @@ namespace ui {
bool m_hidden;
bool m_state; // show or not the text caret
bool m_readonly;
bool m_password;
bool m_recent_focused;
bool m_lock_selection;
bool m_translate_dead_keys;

View File

@ -246,17 +246,14 @@ public:
gfx::Rect bounds() const { return m_bounds; }
void preProcessChar(const base::utf8_const_iterator& it,
const base::utf8_const_iterator& end,
int& chr,
void preProcessChar(const int index,
const int codepoint,
gfx::Color& fg,
gfx::Color& bg,
bool& drawChar,
bool& moveCaret) override {
if (!m_surface)
drawChar = false;
else {
if (m_mnemonic && std::tolower(chr) == m_mnemonic) {
gfx::Color& bg) override {
if (m_surface) {
if (m_mnemonic &&
// TODO use ICU library to lower unicode chars
std::tolower(codepoint) == m_mnemonic) {
m_underscoreColor = fg;
m_mnemonic = 0; // Just one time
}

View File

@ -125,11 +125,6 @@ double Widget::textDouble() const
return strtod(m_text.c_str(), NULL);
}
int Widget::textLength() const
{
return base::utf8_length(text());
}
void Widget::setText(const std::string& text)
{
setTextQuiet(text);

View File

@ -84,7 +84,6 @@ namespace ui {
const std::string& text() const { return m_text; }
int textInt() const;
double textDouble() const;
int textLength() const;
void setText(const std::string& text);
void setTextf(const char* text, ...);
void setTextQuiet(const std::string& text);

View File

@ -1,5 +1,5 @@
# ASEPRITE
# Copyright (C) 2001-2016 David Capello
# Copyright (C) 2001-2017 David Capello
include_directories(.)
@ -85,6 +85,7 @@ if(NOT USE_SHARED_FREETYPE)
endif()
endif()
add_subdirectory(harfbuzz-cmake)
add_subdirectory(simpleini)
# Add cmark without tests

1
third_party/harfbuzz vendored Submodule

@ -0,0 +1 @@
Subproject commit 8a4c80563dbc74608e5c65df395a13ee4840cbf3