| // Copyright 2010-2015, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "win32/tip/tip_ui_renderer_immersive.h" |
| |
| #define _ATL_NO_AUTOMATIC_NAMESPACE |
| #define _WTL_NO_AUTOMATIC_NAMESPACE |
| // Workaround against KB813540 |
| #include <atlbase_mozc.h> |
| #include <atlapp.h> |
| #include <atlmisc.h> |
| |
| #include <memory> |
| |
| #include "base/util.h" |
| #include "session/commands.pb.h" |
| #include "renderer/table_layout.h" |
| #include "renderer/win32/text_renderer.h" |
| |
| namespace mozc { |
| namespace win32 { |
| namespace tsf { |
| |
| namespace { |
| |
| using WTL::CBitmap; |
| using WTL::CBitmapHandle; |
| using WTL::CDC; |
| using WTL::CRect; |
| using std::unique_ptr; |
| |
| using ::mozc::commands::Candidates; |
| using ::mozc::commands::Output; |
| using ::mozc::renderer::TableLayout; |
| using ::mozc::renderer::TableLayoutInterface; |
| using ::mozc::renderer::win32::TextRenderer; |
| using ::mozc::renderer::win32::TextRenderingInfo; |
| typedef ::mozc::commands::Preedit_Segment::Annotation Annotation; |
| typedef ::mozc::commands::Candidates_Candidate Candidate; |
| |
| // DPI-invariant layout size constants in pixel unit. |
| const int kWindowBorder = 2; |
| const int kRowRectPadding = 4; |
| const int kIndicatorWidth = 4; |
| |
| // Color scheme |
| const COLORREF kFrameColor = RGB(0x00, 0x00, 0x00); |
| const COLORREF kSelectedRowBackgroundColor = RGB(0xd1, 0xea, 0xff); |
| const COLORREF kDefaultBackgroundColor = RGB(0xff, 0xff, 0xff); |
| const COLORREF kIndicatorBackgroundColor = RGB(0xe0, 0xe0, 0xe0); |
| const COLORREF kIndicatorColor = RGB(0xb8, 0xb8, 0xb8); |
| |
| // usage type for each column. |
| enum COLUMN_TYPE { |
| COLUMN_GAP1, // padding region |
| COLUMN_CANDIDATE, // show candidate string |
| COLUMN_GAP2, // padding region |
| NUMBER_OF_COLUMNS, // number of columns. (this item should be last) |
| }; |
| |
| CRect ToCRect(const Rect &rect) { |
| return CRect(rect.Left(), rect.Top(), rect.Right(), rect.Bottom()); |
| } |
| |
| // Returns the smallest index of the given candidate list which satisfies |
| // candidates.candidate(i) == |candidate_index|. |
| // This function returns the size of the given candidate list when there |
| // aren't any candidates satisfying the above condition. |
| int GetCandidateArrayIndexByCandidateIndex(const Candidates &candidates, |
| int candidate_index) { |
| for (size_t i = 0; i < candidates.candidate_size(); ++i) { |
| const Candidate &candidate = candidates.candidate(i); |
| |
| if (candidate.index() == candidate_index) { |
| return i; |
| } |
| } |
| |
| return candidates.candidate_size(); |
| } |
| |
| // Returns the smallest index of the given candidate list which satisfies |
| // |candidates.focused_index| == |candidates.candidate(i).index()|. |
| // This function returns the size of the given candidate list when there |
| // aren't any candidates satisfying the above condition. |
| int GetFocusedArrayIndex(const Candidates &candidates) { |
| const int kInvalidIndex = candidates.candidate_size(); |
| |
| if (!candidates.has_focused_index()) { |
| return kInvalidIndex; |
| } |
| |
| const int focused_index = candidates.focused_index(); |
| |
| return GetCandidateArrayIndexByCandidateIndex(candidates, focused_index); |
| } |
| |
| void CalcLayout(const Candidates &candidates, |
| const TextRenderer &text_renderer, |
| TableLayout *table_layout, |
| vector<wstring> *candidate_strings) { |
| table_layout->Initialize(candidates.candidate_size(), NUMBER_OF_COLUMNS); |
| |
| table_layout->SetWindowBorder(kWindowBorder); |
| |
| // Add a positional indicator if candidate list consists of more than one |
| // page. |
| if (candidates.candidate_size() < candidates.size()) { |
| table_layout->SetVScrollBar(kIndicatorWidth); |
| } |
| |
| table_layout->SetRowRectPadding(kRowRectPadding); |
| |
| // put a padding in COLUMN_GAP1. |
| // the width is determined to be equal to the width of " ". |
| const Size gap1_size = |
| text_renderer.MeasureString(TextRenderer::FONTSET_CANDIDATE, L" "); |
| table_layout->EnsureCellSize(COLUMN_GAP1, gap1_size); |
| |
| for (size_t i = 0; i < candidates.candidate_size(); ++i) { |
| wstring candidate_string; |
| const Candidate &candidate = candidates.candidate(i); |
| if (candidate.has_value()) { |
| mozc::Util::UTF8ToWide(candidate.value().c_str(), &candidate_string); |
| } |
| if (candidate.has_annotation()) { |
| const commands::Annotation &annotation = candidate.annotation(); |
| if (annotation.has_prefix()) { |
| wstring annotation_prefix; |
| mozc::Util::UTF8ToWide(annotation.prefix().c_str(), |
| &annotation_prefix); |
| candidate_string = annotation_prefix + candidate_string; |
| } |
| if (annotation.has_suffix()) { |
| wstring annotation_suffix; |
| mozc::Util::UTF8ToWide(annotation.suffix().c_str(), |
| &annotation_suffix); |
| candidate_string += annotation_suffix; |
| } |
| } |
| candidate_strings->push_back(candidate_string); |
| if (!candidate_string.empty()) { |
| const Size rendering_size = text_renderer.MeasureString( |
| TextRenderer::FONTSET_CANDIDATE, candidate_string); |
| table_layout->EnsureCellSize(COLUMN_CANDIDATE, rendering_size); |
| } |
| } |
| |
| // Put a padding in COLUMN_GAP2. |
| const wchar_t *gap2_string = L" "; |
| const Size gap2_size = text_renderer.MeasureString( |
| TextRenderer::FONTSET_CANDIDATE, gap2_string); |
| table_layout->EnsureCellSize(COLUMN_GAP2, gap2_size); |
| table_layout->FreezeLayout(); |
| } |
| |
| CBitmapHandle RenderImpl(const Candidates &candidates, |
| const TableLayout &table_layout, |
| const TextRenderer &text_renderer, |
| const vector<wstring> &candidate_strings) { |
| const int width = table_layout.GetTotalSize().width; |
| const int height = table_layout.GetTotalSize().height; |
| |
| CBitmap bitmap; |
| bitmap.CreateBitmap(width, height, 1, 32, nullptr); |
| CDC dc; |
| dc.CreateCompatibleDC(); |
| CBitmapHandle old_bitmap = dc.SelectBitmap(bitmap); |
| |
| dc.SetBkMode(TRANSPARENT); |
| const CRect client_crect(0, 0, width, height); |
| |
| // Background |
| { |
| dc.FillSolidRect(&client_crect, kDefaultBackgroundColor); |
| } |
| |
| // Focused rectangle |
| { |
| const int focused_array_index = GetFocusedArrayIndex(candidates); |
| if (0 <= focused_array_index && |
| focused_array_index < candidates.candidate_size()) { |
| const commands::Candidates::Candidate &candidate |
| = candidates.candidate(focused_array_index); |
| |
| const CRect selected_rect = |
| ToCRect(table_layout.GetRowRect(focused_array_index)); |
| dc.FillSolidRect(&selected_rect, kSelectedRowBackgroundColor); |
| } |
| } |
| |
| // Candidate strings |
| { |
| const COLUMN_TYPE column_type = COLUMN_CANDIDATE; |
| const TextRenderer::FONT_TYPE font_type = |
| TextRenderer::FONTSET_CANDIDATE; |
| |
| vector<TextRenderingInfo> display_list; |
| for (size_t i = 0; i < candidate_strings.size(); ++i) { |
| const wstring &candidate_string = candidate_strings[i]; |
| const Rect &text_rect = |
| table_layout.GetCellRect(i, column_type); |
| display_list.push_back(TextRenderingInfo(candidate_string, text_rect)); |
| } |
| text_renderer.RenderTextList(dc.m_hDC, display_list, font_type); |
| } |
| |
| // Indicator |
| { |
| const Rect &vscroll_rect = table_layout.GetVScrollBarRect(); |
| |
| if (!vscroll_rect.IsRectEmpty() && candidates.candidate_size() > 0) { |
| const int begin_index = candidates.candidate(0).index(); |
| const int candidates_in_page = candidates.candidate_size(); |
| const int candidates_total = candidates.size(); |
| const int end_index = |
| candidates.candidate(candidates_in_page - 1).index(); |
| |
| const CRect background_crect = ToCRect(vscroll_rect); |
| dc.FillSolidRect(&background_crect, kIndicatorBackgroundColor); |
| |
| const Rect &indicator_rect = |
| table_layout.GetVScrollIndicatorRect( |
| begin_index, end_index, candidates_total); |
| |
| const CRect indicator_crect = ToCRect(indicator_rect); |
| dc.FillSolidRect(&indicator_crect, kIndicatorColor); |
| } |
| } |
| |
| // Edge frame |
| { |
| // DC brush is available in Windows 2000 and later. |
| dc.SetDCBrushColor(kFrameColor); |
| CRect crect = client_crect; |
| for (int i = 0; i < kWindowBorder; ++i) { |
| dc.FrameRect(&crect, |
| static_cast<HBRUSH>(GetStockObject(DC_BRUSH))); |
| crect.DeflateRect(1, 1, 1, 1); |
| } |
| } |
| |
| dc.SelectBitmap(old_bitmap); |
| return bitmap.Detach(); |
| } |
| |
| } // namespace |
| |
| HBITMAP TipUiRendererImmersive::Render( |
| const Candidates &candidates, |
| const renderer::win32::TextRenderer *text_renderer, |
| renderer::TableLayout *table_layout, |
| SIZE *size, int *left_align_offset) { |
| vector<wstring> candidate_strings; |
| CalcLayout(candidates, *text_renderer, table_layout, &candidate_strings); |
| |
| const Size &total_size = table_layout->GetTotalSize(); |
| if (size != nullptr) { |
| size->cx = total_size.width; |
| size->cy = total_size.height; |
| } |
| if (left_align_offset != nullptr) { |
| *left_align_offset = table_layout->GetColumnRect(COLUMN_CANDIDATE).Left(); |
| } |
| return RenderImpl( |
| candidates, *table_layout, *text_renderer, candidate_strings); |
| } |
| |
| } // namespace tsf |
| } // namespace win32 |
| } // namespace mozc |