* Added a string view that shows the character's code on mouse over, the
character gets also highlighted. * Added support for dragging a character. Holding shift (or option) while dragging will copy an UTF-8 hex string instead. * Minor cleanup. * Is there any reason BStringView restricts its maximum width? git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29825 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
751c91b7e1
commit
beedda4c2c
@ -8,41 +8,18 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <Bitmap.h>
|
||||
#include <LayoutUtils.h>
|
||||
#include <ScrollBar.h>
|
||||
|
||||
#include "UnicodeBlocks.h"
|
||||
|
||||
|
||||
void
|
||||
unicode_to_utf8(uint32 c, char** out)
|
||||
{
|
||||
char* s = *out;
|
||||
|
||||
if (c < 0x80)
|
||||
*(s++) = c;
|
||||
else if (c < 0x800) {
|
||||
*(s++) = 0xc0 | (c >> 6);
|
||||
*(s++) = 0x80 | (c & 0x3f);
|
||||
} else if (c < 0x10000) {
|
||||
*(s++) = 0xe0 | (c >> 12);
|
||||
*(s++) = 0x80 | ((c >> 6) & 0x3f);
|
||||
*(s++) = 0x80 | (c & 0x3f);
|
||||
} else if (c <= 0x10ffff) {
|
||||
*(s++) = 0xf0 | (c >> 18);
|
||||
*(s++) = 0x80 | ((c >> 12) & 0x3f);
|
||||
*(s++) = 0x80 | ((c >> 6) & 0x3f);
|
||||
*(s++) = 0x80 | (c & 0x3f);
|
||||
}
|
||||
*out = s;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
CharacterView::CharacterView(const char* name)
|
||||
: BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS),
|
||||
fTargetCommand(0),
|
||||
fClickPoint(-1, 0),
|
||||
fHasCharacter(false),
|
||||
fShowPrivateBlocks(false),
|
||||
fShowContainedBlocksOnly(false)
|
||||
{
|
||||
@ -57,6 +34,14 @@ CharacterView::~CharacterView()
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CharacterView::SetTarget(BMessenger target, uint32 command)
|
||||
{
|
||||
fTarget = target;
|
||||
fTargetCommand = command;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CharacterView::SetCharacterFont(const BFont& font)
|
||||
{
|
||||
@ -91,6 +76,9 @@ CharacterView::ShowContainedBlocksOnly(bool show)
|
||||
bool
|
||||
CharacterView::IsShowingBlock(int32 blockIndex) const
|
||||
{
|
||||
if (blockIndex < 0 || blockIndex >= (int32)kNumUnicodeBlocks)
|
||||
return false;
|
||||
|
||||
if (!fShowPrivateBlocks && kUnicodeBlocks[blockIndex].private_block)
|
||||
return false;
|
||||
|
||||
@ -116,6 +104,51 @@ CharacterView::ScrollTo(int32 blockIndex)
|
||||
}
|
||||
|
||||
|
||||
/*static*/ void
|
||||
CharacterView::UnicodeToUTF8(uint32 c, char* text, size_t textSize)
|
||||
{
|
||||
if (textSize < 5) {
|
||||
if (textSize > 0)
|
||||
text[0] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
char* s = text;
|
||||
|
||||
if (c < 0x80)
|
||||
*(s++) = c;
|
||||
else if (c < 0x800) {
|
||||
*(s++) = 0xc0 | (c >> 6);
|
||||
*(s++) = 0x80 | (c & 0x3f);
|
||||
} else if (c < 0x10000) {
|
||||
*(s++) = 0xe0 | (c >> 12);
|
||||
*(s++) = 0x80 | ((c >> 6) & 0x3f);
|
||||
*(s++) = 0x80 | (c & 0x3f);
|
||||
} else if (c <= 0x10ffff) {
|
||||
*(s++) = 0xf0 | (c >> 18);
|
||||
*(s++) = 0x80 | ((c >> 12) & 0x3f);
|
||||
*(s++) = 0x80 | ((c >> 6) & 0x3f);
|
||||
*(s++) = 0x80 | (c & 0x3f);
|
||||
}
|
||||
|
||||
s[0] = '\0';
|
||||
}
|
||||
|
||||
|
||||
/*static*/ void
|
||||
CharacterView::UnicodeToUTF8Hex(uint32 c, char* text, size_t textSize)
|
||||
{
|
||||
char character[16];
|
||||
CharacterView::UnicodeToUTF8(c, character, sizeof(character));
|
||||
|
||||
int size = 0;
|
||||
for (int32 i = 0; character[i] && size < (int)textSize; i++) {
|
||||
size += snprintf(text + size, textSize - size, "\\x%02x",
|
||||
(uint8)character[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CharacterView::AttachedToWindow()
|
||||
{
|
||||
@ -139,7 +172,6 @@ CharacterView::MinSize()
|
||||
void
|
||||
CharacterView::FrameResized(float width, float height)
|
||||
{
|
||||
puts("hey");
|
||||
_UpdateSize();
|
||||
}
|
||||
|
||||
@ -147,12 +179,14 @@ CharacterView::FrameResized(float width, float height)
|
||||
void
|
||||
CharacterView::MouseDown(BPoint where)
|
||||
{
|
||||
fClickPoint = where;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CharacterView::MouseUp(BPoint where)
|
||||
{
|
||||
fClickPoint.x = -1;
|
||||
}
|
||||
|
||||
|
||||
@ -160,6 +194,65 @@ void
|
||||
CharacterView::MouseMoved(BPoint where, uint32 transit,
|
||||
const BMessage* dragMessage)
|
||||
{
|
||||
if (dragMessage != NULL)
|
||||
return;
|
||||
|
||||
BRect frame;
|
||||
uint32 character;
|
||||
bool hasCharacter = _GetCharacterAt(where, character, &frame);
|
||||
|
||||
if (fHasCharacter && (character != fCurrentCharacter || !hasCharacter))
|
||||
Invalidate(fCurrentCharacterFrame);
|
||||
|
||||
if (hasCharacter && (character != fCurrentCharacter || !fHasCharacter)) {
|
||||
BMessage update(fTargetCommand);
|
||||
update.AddInt32("character", character);
|
||||
fTarget.SendMessage(&update);
|
||||
|
||||
Invalidate(frame);
|
||||
}
|
||||
|
||||
fHasCharacter = hasCharacter;
|
||||
fCurrentCharacter = character;
|
||||
fCurrentCharacterFrame = frame;
|
||||
|
||||
if (fClickPoint.x >= 0 && (fabs(where.x - fClickPoint.x) > 4
|
||||
|| fabs(where.y - fClickPoint.y) > 4)) {
|
||||
// start dragging
|
||||
BPoint offset = fClickPoint - frame.LeftTop();
|
||||
frame.OffsetTo(B_ORIGIN);
|
||||
|
||||
BBitmap* bitmap = new BBitmap(frame, B_BITMAP_ACCEPTS_VIEWS, B_RGBA32);
|
||||
bitmap->Lock();
|
||||
|
||||
BView* view = new BView(frame, "drag", 0, 0);
|
||||
bitmap->AddChild(view);
|
||||
|
||||
view->SetLowColor(B_TRANSPARENT_COLOR);
|
||||
view->FillRect(frame, B_SOLID_LOW);
|
||||
|
||||
// Draw character
|
||||
char text[16];
|
||||
UnicodeToUTF8(character, text, sizeof(text));
|
||||
|
||||
view->SetFont(&fCharacterFont);
|
||||
view->DrawString(text, BPoint((fCharacterWidth - StringWidth(text)) / 2,
|
||||
fCharacterBase));
|
||||
|
||||
view->Sync();
|
||||
bitmap->RemoveChild(view);
|
||||
bitmap->Unlock();
|
||||
|
||||
BMessage drag(B_MIME_DATA);
|
||||
if ((modifiers() & (B_SHIFT_KEY | B_OPTION_KEY)) != 0) {
|
||||
// paste UTF-8 hex string
|
||||
CharacterView::UnicodeToUTF8Hex(character, text, sizeof(text));
|
||||
}
|
||||
drag.AddData("text/plain", B_MIME_DATA, text, strlen(text));
|
||||
|
||||
DragMessage(&drag, bitmap, B_OP_ALPHA, offset);
|
||||
fClickPoint.x = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -173,44 +266,43 @@ CharacterView::MessageReceived(BMessage* message)
|
||||
void
|
||||
CharacterView::Draw(BRect updateRect)
|
||||
{
|
||||
const int32 kStartX = fGap / 2;
|
||||
const int32 kXGap = fGap / 2;
|
||||
|
||||
BFont font;
|
||||
GetFont(&font);
|
||||
int32 y = 2;
|
||||
|
||||
// TODO: jump to the correct start
|
||||
for (uint32 i = 0; i < kNumUnicodeBlocks; i++) {
|
||||
for (int32 i = _BlockAt(updateRect.LeftTop()); i < (int32)kNumUnicodeBlocks;
|
||||
i++) {
|
||||
if (!IsShowingBlock(i))
|
||||
continue;
|
||||
if (fTitleTops[i] > updateRect.bottom)
|
||||
|
||||
int32 y = fTitleTops[i];
|
||||
if (y > updateRect.bottom)
|
||||
break;
|
||||
|
||||
SetHighColor(0, 0, 0);
|
||||
SetHighColor(0, 0, 0);
|
||||
DrawString(kUnicodeBlocks[i].name, BPoint(3, y + fTitleBase));
|
||||
|
||||
y += fTitleHeight;
|
||||
int32 x = kStartX;
|
||||
int32 x = kXGap;
|
||||
SetFont(&fCharacterFont);
|
||||
|
||||
char character[16];
|
||||
for (uint32 c = kUnicodeBlocks[i].start; c <= kUnicodeBlocks[i].end;
|
||||
c++) {
|
||||
if (y + fCharacterHeight > updateRect.top
|
||||
&& y < updateRect.bottom) {
|
||||
#if 0
|
||||
// Stroke frame around the charater
|
||||
SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT));
|
||||
StrokeRect(BRect(x, y, x + fCharacterWidth,
|
||||
y + fCharacterHeight - fGap));
|
||||
// Stroke frame around the active character
|
||||
if (fHasCharacter && fCurrentCharacter == c) {
|
||||
SetHighColor(tint_color(ViewColor(), 1.05f));
|
||||
FillRect(BRect(x, y, x + fCharacterWidth,
|
||||
y + fCharacterHeight - fGap));
|
||||
|
||||
SetHighColor(0, 0, 0);
|
||||
#endif
|
||||
SetHighColor(0, 0, 0);
|
||||
}
|
||||
|
||||
// Draw character
|
||||
char* end = character;
|
||||
unicode_to_utf8(c, &end);
|
||||
end[0] = '\0';
|
||||
char character[16];
|
||||
UnicodeToUTF8(c, character, sizeof(character));
|
||||
|
||||
DrawString(character,
|
||||
BPoint(x + (fCharacterWidth - StringWidth(character)) / 2,
|
||||
@ -218,13 +310,13 @@ CharacterView::Draw(BRect updateRect)
|
||||
}
|
||||
|
||||
x += fCharacterWidth + fGap;
|
||||
if (x + fCharacterWidth + fGap / 2 >= fDataRect.right) {
|
||||
if (x + fCharacterWidth + kXGap >= fDataRect.right) {
|
||||
y += fCharacterHeight;
|
||||
x = kStartX;
|
||||
x = kXGap;
|
||||
}
|
||||
}
|
||||
|
||||
if (x != kStartX)
|
||||
if (x != kXGap)
|
||||
y += fCharacterHeight;
|
||||
y += fTitleGap;
|
||||
|
||||
@ -240,6 +332,68 @@ CharacterView::DoLayout()
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
CharacterView::_BlockAt(BPoint point)
|
||||
{
|
||||
// TODO: use binary search
|
||||
for (uint32 i = 0; i < kNumUnicodeBlocks; i++) {
|
||||
if (!IsShowingBlock(i))
|
||||
continue;
|
||||
|
||||
if (fTitleTops[i] < point.y
|
||||
&& (i == kNumUnicodeBlocks - 1 || fTitleTops[i + 1] > point.y))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
CharacterView::_GetCharacterAt(BPoint point, uint32& character, BRect* _frame)
|
||||
{
|
||||
int32 i = _BlockAt(point);
|
||||
if (i == -1)
|
||||
return false;
|
||||
|
||||
int32 y = fTitleTops[i] + fTitleHeight;
|
||||
if (y > point.y)
|
||||
return false;
|
||||
|
||||
const int32 startX = fGap / 2;
|
||||
if (startX > point.x)
|
||||
return false;
|
||||
|
||||
int32 endX = startX + fCharactersPerLine * (fCharacterWidth + fGap);
|
||||
if (endX < point.x)
|
||||
return false;
|
||||
|
||||
for (uint32 c = kUnicodeBlocks[i].start; c <= kUnicodeBlocks[i].end;
|
||||
c += fCharactersPerLine, y += fCharacterHeight) {
|
||||
if (y + fCharacterHeight <= point.y)
|
||||
continue;
|
||||
|
||||
int32 pos = (int32)((point.x - startX) / (fCharacterWidth + fGap));
|
||||
if (c + pos > kUnicodeBlocks[i].end)
|
||||
return false;
|
||||
|
||||
// Found character at position
|
||||
|
||||
character = c + pos;
|
||||
|
||||
if (_frame != NULL) {
|
||||
_frame->Set(startX + pos * (fCharacterWidth + fGap),
|
||||
y, startX + (pos + 1) * (fCharacterWidth + fGap) - 1,
|
||||
y + fCharacterHeight);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CharacterView::_UpdateSize()
|
||||
{
|
||||
@ -276,9 +430,9 @@ CharacterView::_UpdateSize()
|
||||
fDataRect.right = bounds.Width();
|
||||
fDataRect.bottom = 0;
|
||||
|
||||
int32 charactersPerLine = int32(bounds.Width() / (fGap + fCharacterWidth));
|
||||
if (charactersPerLine == 0)
|
||||
charactersPerLine = 1;
|
||||
fCharactersPerLine = int32(bounds.Width() / (fGap + fCharacterWidth));
|
||||
if (fCharactersPerLine == 0)
|
||||
fCharactersPerLine = 1;
|
||||
|
||||
for (uint32 i = 0; i < kNumUnicodeBlocks; i++) {
|
||||
fTitleTops[i] = (int32)ceilf(fDataRect.bottom);
|
||||
@ -286,8 +440,8 @@ CharacterView::_UpdateSize()
|
||||
if (!IsShowingBlock(i))
|
||||
continue;
|
||||
|
||||
int32 lines = (kUnicodeBlocks[i].Count() + charactersPerLine - 1)
|
||||
/ charactersPerLine;
|
||||
int32 lines = (kUnicodeBlocks[i].Count() + fCharactersPerLine - 1)
|
||||
/ fCharactersPerLine;
|
||||
fDataRect.bottom += lines * fCharacterHeight + fTitleHeight + fTitleGap;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#define CHARACTER_VIEW_H
|
||||
|
||||
|
||||
#include <Messenger.h>
|
||||
#include <View.h>
|
||||
|
||||
|
||||
@ -14,6 +15,8 @@ public:
|
||||
CharacterView(const char* name);
|
||||
virtual ~CharacterView();
|
||||
|
||||
void SetTarget(BMessenger target, uint32 command);
|
||||
|
||||
void SetCharacterFont(const BFont& font);
|
||||
const BFont& CharacterFont() { return fCharacterFont; }
|
||||
|
||||
@ -29,6 +32,11 @@ public:
|
||||
|
||||
void ScrollTo(int32 blockIndex);
|
||||
|
||||
static void UnicodeToUTF8(uint32 c, char* text,
|
||||
size_t textSize);
|
||||
static void UnicodeToUTF8Hex(uint32 c, char* text,
|
||||
size_t textSize);
|
||||
|
||||
protected:
|
||||
virtual void AttachedToWindow();
|
||||
virtual void DetachedFromWindow();
|
||||
@ -48,15 +56,25 @@ protected:
|
||||
virtual void DoLayout();
|
||||
|
||||
private:
|
||||
bool _IncludeBlock(int32 index);
|
||||
int32 _BlockAt(BPoint point);
|
||||
bool _GetCharacterAt(BPoint point, uint32& character,
|
||||
BRect* _frame = NULL);
|
||||
void _UpdateSize();
|
||||
|
||||
private:
|
||||
BMessenger fTarget;
|
||||
uint32 fTargetCommand;
|
||||
BPoint fClickPoint;
|
||||
bool fHasCharacter;
|
||||
uint32 fCurrentCharacter;
|
||||
BRect fCurrentCharacterFrame;
|
||||
|
||||
bool fShowPrivateBlocks;
|
||||
bool fShowContainedBlocksOnly;
|
||||
|
||||
BRect fDataRect;
|
||||
BFont fCharacterFont;
|
||||
int32 fCharactersPerLine;
|
||||
int32 fCharacterWidth;
|
||||
int32 fCharacterHeight;
|
||||
int32 fCharacterBase;
|
||||
|
@ -21,12 +21,14 @@
|
||||
#include <ScrollView.h>
|
||||
#include <Slider.h>
|
||||
#include <SplitLayoutBuilder.h>
|
||||
#include <StringView.h>
|
||||
|
||||
#include "CharacterView.h"
|
||||
#include "UnicodeBlocks.h"
|
||||
|
||||
|
||||
static const uint32 kMsgUnicodeBlockSelected = 'unbs';
|
||||
static const uint32 kMsgCharacterChanged = 'chch';
|
||||
static const uint32 kMsgFontSelected = 'fnts';
|
||||
static const uint32 kMsgFontSizeChanged = 'fsch';
|
||||
static const uint32 kMsgPrivateBlocks = 'prbl';
|
||||
@ -84,6 +86,7 @@ CharacterWindow::CharacterWindow()
|
||||
fUnicodeBlockView, 0, false, true);
|
||||
|
||||
fCharacterView = new CharacterView("characters");
|
||||
fCharacterView->SetTarget(this, kMsgCharacterChanged);
|
||||
|
||||
bool show;
|
||||
if (settings.FindBool("show private blocks", &show) == B_OK)
|
||||
@ -118,13 +121,18 @@ CharacterWindow::CharacterWindow()
|
||||
new BMessage(kMsgFontSizeChanged), kMinFontSize, kMaxFontSize);
|
||||
fFontSizeSlider->SetValue(fontSize);
|
||||
|
||||
fCodeView = new BStringView("code", "-");
|
||||
fCodeView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED,
|
||||
fCodeView->PreferredSize().Height()));
|
||||
|
||||
AddChild(BGroupLayoutBuilder(B_VERTICAL)
|
||||
.Add(menuBar)
|
||||
.Add(BGroupLayoutBuilder(B_HORIZONTAL, 10)//BSplitLayoutBuilder()
|
||||
.Add(unicodeScroller)
|
||||
.Add(BGroupLayoutBuilder(B_VERTICAL, 10)
|
||||
.Add(characterScroller)
|
||||
.Add(fFontSizeSlider))
|
||||
.Add(fFontSizeSlider)
|
||||
.Add(fCodeView))
|
||||
.SetInsets(10, 10, 10, 10)));
|
||||
|
||||
// Add menu
|
||||
@ -182,6 +190,24 @@ CharacterWindow::MessageReceived(BMessage* message)
|
||||
break;
|
||||
}
|
||||
|
||||
case kMsgCharacterChanged:
|
||||
{
|
||||
uint32 character;
|
||||
if (message->FindInt32("character", (int32*)&character) != B_OK)
|
||||
break;
|
||||
|
||||
char utf8Hex[32];
|
||||
CharacterView::UnicodeToUTF8Hex(character, utf8Hex,
|
||||
sizeof(utf8Hex));
|
||||
|
||||
char text[128];
|
||||
snprintf(text, sizeof(text), "Code: %#lx (%ld), UTF-8: %s",
|
||||
character, character, utf8Hex);
|
||||
|
||||
fCodeView->SetText(text);
|
||||
break;
|
||||
}
|
||||
|
||||
case kMsgFontSelected:
|
||||
{
|
||||
BMenuItem* item;
|
||||
|
@ -14,6 +14,7 @@ class BListView;
|
||||
class BMenu;
|
||||
class BMenuItem;
|
||||
class BSlider;
|
||||
class BStringView;
|
||||
class CharacterView;
|
||||
|
||||
|
||||
@ -40,6 +41,7 @@ private:
|
||||
CharacterView* fCharacterView;
|
||||
BMenuItem* fSelectedFontItem;
|
||||
BSlider* fFontSizeSlider;
|
||||
BStringView* fCodeView;
|
||||
};
|
||||
|
||||
#endif // CHARACTER_WINDOW_H
|
||||
|
Loading…
Reference in New Issue
Block a user