when you erase a coloured text. By default, the control sets the new entered text colour back to that was recently erased. how can you avoid that? do you need to check each character style before you type?
UPDATE:
I'm trying to set the text color like this.
SendMessage(hEdit, EM_SETSEL, start_pos, end_pos); //select text for coloring
CHARFORMAT cf;
memset( &cf, 0, sizeof cf );
cf.cbSize = sizeof cf;
cf.dwMask = CFM_COLOR;
cf.crTextColor = RGB(255,0,0);
SendMessage( hEdit , EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf);
SendMessage(hEdit, EM_SETSEL, -1, 0 ); //deselect text
cf.crTextColor = RGB(0,0,0); //reset colour
SendMessage( hEdit , EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf); //set colour
Your question is quite unclear. Wild stab at it: you lose all formatting when you assign the Text property. Be sure to use AppendText() instead. And to set the SelectionColor and SelectionBackColor properties back to what it was after colorizing any text so that newly entered text gets the preferred default colors.
Related
First of all sorry for my bad English. But i can't solve my problem for the last few days. I am working on a simple text editor that use DirectWrite to render text. When i render my buffer, that contains a text, direct2d wraps my words, but this doesn't work when i type space chars ' ' or tabs '\t'. My cursor is going outside the editor's window and doesn't "jumt" to the new line.
int RenderSystem::Init(
Settings* set,
HWND hwnd
)
{
if (system_is_init)
return 0;
settings = set;
D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
&factory
);
DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
(IUnknown**)(&write_factory)
);
LoadFontCollection(L"Fonts/liberation-mono/liberation-mono.ttf");
write_factory->CreateTextFormat(
L"Liberation Mono",
font_collection,
DWRITE_FONT_WEIGHT_REGULAR,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
(float)settings->font_size,
L"en-us",
&text_format
);
text_format->SetWordWrapping(
DWRITE_WORD_WRAPPING_WRAP
);
text_renderer = new BasicTextRenderer();
return 0;
}
void RenderSystem::Render(
ID2D1HwndRenderTarget* render_target,
Buffer* buffer
)
{
render_target->CreateSolidColorBrush(
settings->text_foreground_color,
&text_foreground_brush
);
render_target->CreateSolidColorBrush(
settings->cursor_background_color,
&cursor_background_brush
);
render_target->CreateSolidColorBrush(
settings->cursor_foreground_colot,
&cursor_foreground_brush
);
D2D1_SIZE_U render_target_size = render_target->GetPixelSize();
IDWriteTextLayout* text_layout;
UINT size;
WCHAR* text = buffer->GetData(size); //Here i get text from my buffer
write_factory->CreateTextLayout(
text,
size,
text_format,
(float)render_target_size.width,
(float)render_target_size.height,
&text_layout
);
delete[] text;
DrawingContext* drawing_context = new DrawingContext(
render_target,
text_foreground_brush
);
DrawingEffect* cursor_effect = new DrawingEffect(
cursor_foreground_brush,
cursor_background_brush
);
DWRITE_TEXT_RANGE text_range;
text_range.startPosition = buffer->GetCursorPos();
text_range.length = 1;
text_layout->SetDrawingEffect(
cursor_effect,
text_range
);
render_target->BeginDraw();
render_target->Clear(settings->background_color);
text_layout->Draw(
drawing_context,
text_renderer,
0,
0
);
render_target->EndDraw();
text_layout->Release();
text_foreground_brush->Release();
cursor_background_brush->Release();
cursor_foreground_brush->Release();
delete drawing_context;
}
I hope someone will understand what my problem is... Thank you for any help.
I don't think it's necessarily wrong. It's just the way DirectWrite treats trailing spacing characters - they do not contribute to wrapping line width. You can try different wrapping modes, but what you probably need is to implement layout logic yourself, using cluster metrics and line breaking positions from DirectWrite API.
DirectWrite text layout formatting capabilities are enough for general UI text rendering, anything more complicated requires lower level DirectWrite API (text analyzer) and more complicated logic at application side.
I'm trying to create a dynamic dialog, which can be made RTL depending on the language. But I have the following issue: whenever I change the RTL style of the combo box, the text comes up reversed. I tried using functions such as InvalidateRect, RedrawWindow, etc., but couldn't make it work correctly.
Relevant code (WinAPI with WTL):
CComboBox combo = hWndCtl;
if(combo.GetCurSel() == 0)
combo.ModifyStyleEx(WS_EX_LAYOUTRTL, 0);
else
combo.ModifyStyleEx(0, WS_EX_LAYOUTRTL);
A demo project: here.
A demonstration of the issue:
It seems you are responding to CBN_SELCHANGE notification. This is notification is sent after combobox sets the text in its editbox.
You should respond to CBN_SELENDOK instead. CBN_SELENDOK is sent before CBN_SELCHANGE, this gives you time to modify the style before combobox sets the text.
switch (HIWORD(wParam))
{
case CBN_SELENDOK:// CBN_SELCHANGE:
if (SendMessage(hComboBox, CB_GETCURSEL, 0, 0) == 0)
ModifyStyleEx(hComboBox, WS_EX_LAYOUTRTL, 0);
else
ModifyStyleEx(hComboBox, 0, WS_EX_LAYOUTRTL);
break;
default:break;
}
Edit: Windows 10 has fade in/out effect. If you change the combo selection with keyboard, while the color is fading, the text is still goes backward.
ComboBox has an edit control which might be causing this problem. It's better to use WS_EX_RIGHT | WS_EX_RTLREADING instead of WS_EX_LAYOUTRTL. This will also work with CBN_SELCHANGE.
case CBN_SELENDOK: //(or CBN_SELCHANGE)
if (SendMessage(hComboBox, CB_GETCURSEL, 0, 0) == 0)
ModifyStyleEx(hComboBox, WS_EX_RIGHT | WS_EX_RTLREADING, 0);
else
ModifyStyleEx(hComboBox, 0, WS_EX_RIGHT | WS_EX_RTLREADING);
break;
I need to make tooltip a little bit right and lower to mouse cursor, but i can't do it in any way, tried different coordintaes but nothing seems to work. Where is the problem? Thank you.
// Add the new tooltip (if available)
if (m_LastToolTipRow!=-1 && m_LastToolTipRow!=-1)
{
// Not using CToolTipCtrl::AddTool() because it redirects the messages to CListCtrl parent
TOOLINFO ti = {0};
ti.cbSize = sizeof(TOOLINFO);
ti.uFlags = TTF_IDISHWND | TTF_TRANSPARENT; // Indicate that uId is handle to a control
ti.uId = (UINT_PTR)m_hWnd; // Handle to the control
ti.hwnd = m_hWnd; // Handle to window to receive the tooltip-messages
ti.hinst = AfxGetInstanceHandle();
ti.lpszText = LPSTR_TEXTCALLBACK;
m_OwnToolTipCtrl.SendMessage(TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
m_OwnToolTipCtrl.SendMessage(TTM_TRACKPOSITION, 0, (LPARAM)MAKELPARAM(pt.x + 100, pt.y + 100));
m_OwnToolTipCtrl.SendMessage(TTM_TRACKACTIVATE, true, (LPARAM)&ti);
m_OwnToolTipCtrl.Activate(TRUE);
//Multiline
m_OwnToolTipCtrl.SetMaxTipWidth(256);
//m_OwnToolTipCtrl.SetMaxTipWidth(SHRT_MAX);
}
TTF_IDISHWND
Indicates that the uId member is the window handle to the tool. If this flag is not set, uId is the tool's identifier.
According to this, the window with m_hWnd handle is the one that shows the tooltip and you can position the window itself. If you meant a tooltip separate to that window than there is a principal problem there.
I have a program winapi (C++) nearly complete. The problem now is I want to set fill color of text box and that textbox is readonly. When I set that textbox readonly, I can't fill it white. And when I don't, it can be filled with white.
This is how I create a textbox:
CreateWindow(L"EDIT", text, WS_CHILD|WS_VISIBLE|WS_BORDER|ES_READONLY|ES_RIGHT, left, top, width, height, hWnd, (HMENU)ID, hInst, NULL)
And this code is in WinProc:
case WM_CTLCOLOREDIT:
SetTextColor((HDC)wParam,RGB(0,0,255));
SetBkColor((HDC)wParam,RGB(255,255,255));
SetBkMode((HDC)wParam, TRANSPARENT);
return (LRESULT)GetStockObject(WHITE_BRUSH);
You'll want to use WM_CTLCOLORSTATIC for read-only text boxes; see the docs for WM_CTLCOLOREDIT.
As per HerrJoebob's solution, but you need to differentiate between static's and edit's: (untested code, but the idea is there)
case WM_CTLCOLORSTATIC:
{
TCHAR senderClass[256] ;
GetClassName((HWND)lParam, senderClass, 256);
if (_tscmp(senderClass, WC_EDIT)
{
//Code to change the colour of edit controls
}
}
break;
I have an EDIT control created like this:
hwndEDIT_5 = CreateWindowEx (
0, "EDIT", NULL,
WS_VSCROLL | WS_BORDER | WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_READONLY,
135, 450, 555, 200,
h2, ( HMENU ) ID_EDIT_CONSOLE,
h1, NULL
);
As you can see it is a read-only EDIT area where multi lines text can be displayed. It is supposed to be a console where I can display some information for users when they use the program. I would like the text area to automatically scroll to the bottom-most entry (the newest one) whenever a new line (or message for an user) is added. I've implemented this:
SetDlgItemText ( h2, ID_EDIT_CONSOLE, ch_s );
SCROLLINFO scr;
SCROLLINFO * scr_p = &scr;
scr.cbSize = sizeof ( SCROLLINFO );
scr.fMask = SIF_RANGE;
GetScrollInfo ( GetDlgItem ( h2, ID_EDIT_CONSOLE), SB_VERT, scr_p );
int mmax = scr.nMax;
scr.fMask = SIF_POS;
scr.nPos = mmax;
SetScrollInfo ( GetDlgItem ( h2, ID_EDIT_CONSOLE), SB_VERT, scr_p, TRUE );
That code is scrolling vertical scrollbar to the end of an EDIT control after adding new msg and it works great, the scrollbar gets scrolled but the text still remains visible from the beginning - it rewinds to the beginning after addition while scrollbar rewinds to the bottom. How to make it properly?
Last but not least - this is might be important - in order to display a message firstly I capture the text that is already displayed by using:
GetDlgItemText ( h2, ID_EDIT_CONSOLE, buf, len + 1 );
then I convert buf into string and add to that string a new message that I want to display. Then I convert it back to char array and set it up with SetDlgItemText. I seperate lines by using \r\n. I've coded it that way because I didn't know how to add a line to an EDIT control in different way than using SetDlgItemText. And it adds only one entry AFAIK - if used twice I will not come up with two entries added to an EDIT control, but the first one will get replaced by second function call.
Don't use SetScrollInfo. Use SendMessage() with the EM_LINESCROLL message, sending the message to the edit control's window handle.
SendMessage(MemoHwnd, EM_LINESCROLL, 0, NumLinesToScroll);
The documentation says:
The control does not scroll vertically past the last line of text in the edit control. If the current line plus the number of lines specified by the lParam parameter exceeds the total number of lines in the edit control, the value is adjusted so that the last line of the edit control is scrolled to the top of the edit-control window.
I had the same problem and solved it with Jerry Coffin's answer and some research.
This is the way I use now:
string text = "Append this text";
SendMessageA(hEdit, EM_SETSEL, 0, -1); //Select all
SendMessageA(hEdit, EM_SETSEL, -1, -1);//Unselect and stay at the end pos
SendMessageA(hEdit, EM_REPLACESEL, 0, (LPARAM)(text.c_str())); //append text to current pos and scroll down
If needed: To scroll at the end of Edit Control without appending text:
SendMessageA(hEdit, EM_SETSEL, 0, -1); //Select all.
SendMessageA(hEdit, EM_SETSEL, -1, -1);//Unselect and stay at the end pos
SendMessageA(hEdit, EM_SCROLLCARET, 0, 0); //Set scrollcaret to the current Pos
You can add text by setting the beginning and end of the selection to the end of the text in the control (EM_SETSEL), then replacing the (empty) selection with your new text (EM_REPLACESEL).
Scrolling to the bottom can be done with EM_SCROLLCARET after the caret (the selection) is at the end of the text. There are other ways, but if you're doing it immediately after adding text, this is probably the easiest.
in my case I had a multi line string and Ken White's idea worked very well:
HWND hEdit = this->GetDlgItem(IDC_EDIT_LOG)->m_hWnd;
if (hEdit)
{
int lineCount = m_strClientLog.Replace(_T("\n"), _T("\n"));
::SendMessage(hEdit, EM_LINESCROLL, 0, lineCount);
}
for MFC projects you can use:
mLoggingTextCtl.SendMessage(EM_SETSEL, 0, -1); //Select all.
mLoggingTextCtl.SendMessage(EM_SETSEL, -1, -1);//Unselect and stay at the end pos
mLoggingTextCtl.SendMessage(EM_SCROLLCARET, 0, 0); //Set scrollcaret to the current Pos