/*********************************************************************** THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY DEVELOPER: Zihan Chen(vczh) ***********************************************************************/ #include "GacUI.h" #ifndef VCZH_DEBUG_NO_REFLECTION #include "GacUIReflection.h" #endif /*********************************************************************** .\GRAPHICSCOMPOSITION\GUIGRAPHICSEVENTRECEIVER.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace compositions { /*********************************************************************** Event Receiver ***********************************************************************/ GuiGraphicsEventReceiver::GuiGraphicsEventReceiver(GuiGraphicsComposition* _sender) :sender(_sender) ,leftButtonDown(_sender) ,leftButtonUp(_sender) ,leftButtonDoubleClick(_sender) ,middleButtonDown(_sender) ,middleButtonUp(_sender) ,middleButtonDoubleClick(_sender) ,rightButtonDown(_sender) ,rightButtonUp(_sender) ,rightButtonDoubleClick(_sender) ,horizontalWheel(_sender) ,verticalWheel(_sender) ,mouseMove(_sender) ,mouseEnter(_sender) ,mouseLeave(_sender) ,previewKey(_sender) ,keyDown(_sender) ,keyUp(_sender) ,systemKeyDown(_sender) ,systemKeyUp(_sender) ,previewCharInput(_sender) ,charInput(_sender) ,gotFocus(_sender) ,lostFocus(_sender) ,caretNotify(_sender) ,clipboardNotify(_sender) { } GuiGraphicsEventReceiver::~GuiGraphicsEventReceiver() { } GuiGraphicsComposition* GuiGraphicsEventReceiver::GetAssociatedComposition() { return sender; } } } } /*********************************************************************** .\GRAPHICSCOMPOSITION\GUIGRAPHICSSPECIALIZEDCOMPOSITION.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace compositions { /*********************************************************************** GuiSideAlignedComposition ***********************************************************************/ GuiSideAlignedComposition::GuiSideAlignedComposition() :direction(Top) ,maxLength(10) ,maxRatio(1.0) { } GuiSideAlignedComposition::~GuiSideAlignedComposition() { } GuiSideAlignedComposition::Direction GuiSideAlignedComposition::GetDirection() { return direction; } void GuiSideAlignedComposition::SetDirection(Direction value) { direction = value; InvokeOnCompositionStateChanged(); } vint GuiSideAlignedComposition::GetMaxLength() { return maxLength; } void GuiSideAlignedComposition::SetMaxLength(vint value) { if (value < 0) value = 0; maxLength = value; InvokeOnCompositionStateChanged(); } double GuiSideAlignedComposition::GetMaxRatio() { return maxRatio; } void GuiSideAlignedComposition::SetMaxRatio(double value) { maxRatio = value < 0 ? 0 : value>1 ? 1 : value; InvokeOnCompositionStateChanged(); } bool GuiSideAlignedComposition::IsSizeAffectParent() { return false; } Rect GuiSideAlignedComposition::GetBounds() { Rect result; GuiGraphicsComposition* parent = GetParent(); if (parent) { Rect bounds = parent->GetBounds(); vint w = (vint)(bounds.Width()*maxRatio); vint h = (vint)(bounds.Height()*maxRatio); if (w > maxLength) w = maxLength; if (h > maxLength) h = maxLength; switch (direction) { case Left: { bounds.x2 = bounds.x1 + w; } break; case Top: { bounds.y2 = bounds.y1 + h; } break; case Right: { bounds.x1 = bounds.x2 - w; } break; case Bottom: { bounds.y1 = bounds.y2 - h; } break; } result = bounds; } UpdatePreviousBounds(result); return result; } /*********************************************************************** GuiPartialViewComposition ***********************************************************************/ GuiPartialViewComposition::GuiPartialViewComposition() :wRatio(0.0) ,wPageSize(1.0) ,hRatio(0.0) ,hPageSize(1.0) { } GuiPartialViewComposition::~GuiPartialViewComposition() { } double GuiPartialViewComposition::GetWidthRatio() { return wRatio; } double GuiPartialViewComposition::GetWidthPageSize() { return wPageSize; } double GuiPartialViewComposition::GetHeightRatio() { return hRatio; } double GuiPartialViewComposition::GetHeightPageSize() { return hPageSize; } void GuiPartialViewComposition::SetWidthRatio(double value) { wRatio = value; InvokeOnCompositionStateChanged(); } void GuiPartialViewComposition::SetWidthPageSize(double value) { wPageSize = value; InvokeOnCompositionStateChanged(); } void GuiPartialViewComposition::SetHeightRatio(double value) { hRatio = value; InvokeOnCompositionStateChanged(); } void GuiPartialViewComposition::SetHeightPageSize(double value) { hPageSize = value; InvokeOnCompositionStateChanged(); } bool GuiPartialViewComposition::IsSizeAffectParent() { return false; } Rect GuiPartialViewComposition::GetBounds() { Rect result; GuiGraphicsComposition* parent = GetParent(); if (parent) { Rect bounds = parent->GetBounds(); vint w = bounds.Width(); vint h = bounds.Height(); vint pw = (vint)(wPageSize*w); vint ph = (vint)(hPageSize*h); vint ow = preferredMinSize.x - pw; if (ow < 0) ow = 0; vint oh = preferredMinSize.y - ph; if (oh < 0) oh = 0; w -= ow; h -= oh; pw += ow; ph += oh; result = Rect(Point((vint)(wRatio*w), (vint)(hRatio*h)), Size(pw, ph)); } UpdatePreviousBounds(result); return result; } } } } /*********************************************************************** .\GRAPHICSCOMPOSITION\GUIGRAPHICSSTACKCOMPOSITION.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace compositions { /*********************************************************************** GuiStackComposition ***********************************************************************/ void GuiStackComposition::UpdateStackItemBounds() { if (stackItemBounds.Count() != stackItems.Count()) { stackItemBounds.Resize(stackItems.Count()); } stackItemTotalSize = Size(0, 0); Point offset; for (vint i = 0; i < stackItems.Count(); i++) { vint offsetX = 0; vint offsetY = 0; Size itemSize = stackItems[i]->GetMinSize(); stackItemBounds[i] = Rect(offset, itemSize); #define ACCUMULATE(U, V) \ { \ if (stackItemTotalSize.V < itemSize.V) \ { \ stackItemTotalSize.V = itemSize.V; \ } \ if (i > 0) \ { \ stackItemTotalSize.U += padding; \ } \ stackItemTotalSize.U += itemSize.U; \ } \ switch (direction) { case GuiStackComposition::Horizontal: case GuiStackComposition::ReversedHorizontal: ACCUMULATE(x, y) break; case GuiStackComposition::Vertical: case GuiStackComposition::ReversedVertical: ACCUMULATE(y, x) break; } #undef ACCUMULATE offset.x += itemSize.x + padding; offset.y += itemSize.y + padding; } EnsureStackItemVisible(); } void GuiStackComposition::EnsureStackItemVisible() { #define ADJUSTMENT(U, V) \ if (itemBounds.U() <= 0) \ { \ adjustment -= itemBounds.U(); \ } \ else \ { \ vint overflow = itemBounds.V() - previousBounds.V(); \ if (overflow > 0) \ { \ adjustment -= overflow; \ } \ } \ if (ensuringVisibleStackItem) { Rect itemBounds = ensuringVisibleStackItem->GetBounds(); switch (direction) { case Horizontal: case ReversedHorizontal: ADJUSTMENT(Left, Right) break; case Vertical: case ReversedVertical: ADJUSTMENT(Top, Bottom) break; } } InvokeOnCompositionStateChanged(); #undef ADJUSTMENT } void GuiStackComposition::OnBoundsChanged(GuiGraphicsComposition* sender, GuiEventArgs& arguments) { EnsureStackItemVisible(); } void GuiStackComposition::OnChildInserted(GuiGraphicsComposition* child) { GuiBoundsComposition::OnChildInserted(child); GuiStackItemComposition* item = dynamic_cast(child); if (item) { if (!stackItems.Contains(item)) { stackItems.Add(item); } UpdateStackItemBounds(); } } void GuiStackComposition::OnChildRemoved(GuiGraphicsComposition* child) { GuiBoundsComposition::OnChildRemoved(child); GuiStackItemComposition* item = dynamic_cast(child); if (item) { stackItems.Remove(item); if (item == ensuringVisibleStackItem) { ensuringVisibleStackItem = 0; } UpdateStackItemBounds(); } } GuiStackComposition::GuiStackComposition() { BoundsChanged.AttachMethod(this, &GuiStackComposition::OnBoundsChanged); } GuiStackComposition::~GuiStackComposition() { } const GuiStackComposition::ItemCompositionList& GuiStackComposition::GetStackItems() { return stackItems; } bool GuiStackComposition::InsertStackItem(vint index, GuiStackItemComposition* item) { index = stackItems.Insert(index, item); if (!AddChild(item)) { stackItems.RemoveAt(index); return false; } else { return true; } } GuiStackComposition::Direction GuiStackComposition::GetDirection() { return direction; } void GuiStackComposition::SetDirection(Direction value) { direction = value; EnsureStackItemVisible(); } vint GuiStackComposition::GetPadding() { return padding; } void GuiStackComposition::SetPadding(vint value) { padding = value; EnsureStackItemVisible(); } void GuiStackComposition::ForceCalculateSizeImmediately() { GuiBoundsComposition::ForceCalculateSizeImmediately(); UpdateStackItemBounds(); } Size GuiStackComposition::GetMinPreferredClientSize() { Size minSize = GuiBoundsComposition::GetMinPreferredClientSize(); if (GetMinSizeLimitation() == GuiGraphicsComposition::LimitToElementAndChildren) { if (!ensuringVisibleStackItem || direction == Vertical || direction == ReversedVertical) { if (minSize.x < stackItemTotalSize.x) { minSize.x = stackItemTotalSize.x; } } if (!ensuringVisibleStackItem || direction == Horizontal || direction == ReversedHorizontal) { if (minSize.y < stackItemTotalSize.y) { minSize.y = stackItemTotalSize.y; } } } vint x = 0; vint y = 0; if (extraMargin.left > 0) x += extraMargin.left; if (extraMargin.right > 0) x += extraMargin.right; if (extraMargin.top > 0) y += extraMargin.top; if (extraMargin.bottom > 0) y += extraMargin.bottom; return minSize + Size(x, y); } Rect GuiStackComposition::GetBounds() { for (vint i = 0; i < stackItems.Count(); i++) { if (stackItemBounds[i].GetSize() != stackItems[i]->GetMinSize()) { UpdateStackItemBounds(); break; } } Rect bounds = GuiBoundsComposition::GetBounds(); previousBounds = bounds; UpdatePreviousBounds(previousBounds); return bounds; } Margin GuiStackComposition::GetExtraMargin() { return extraMargin; } void GuiStackComposition::SetExtraMargin(Margin value) { extraMargin=value; EnsureStackItemVisible(); } bool GuiStackComposition::IsStackItemClipped() { Rect clientArea = GetClientArea(); switch(direction) { case Horizontal: case ReversedHorizontal: { vint width = stackItemTotalSize.x + (extraMargin.left > 0 ? extraMargin.left : 0) + (extraMargin.right > 0 ? extraMargin.right : 0) ; return width > clientArea.Width(); } break; case Vertical: case ReversedVertical: { vint height = stackItemTotalSize.y + (extraMargin.top > 0 ? extraMargin.top : 0) + (extraMargin.bottom > 0 ? extraMargin.bottom : 0) ; return height > clientArea.Height(); } break; } return false; } bool GuiStackComposition::EnsureVisible(vint index) { if (0 <= index && index < stackItems.Count()) { ensuringVisibleStackItem = stackItems[index]; } else { ensuringVisibleStackItem = 0; } EnsureStackItemVisible(); return ensuringVisibleStackItem != 0; } /*********************************************************************** GuiStackItemComposition ***********************************************************************/ void GuiStackItemComposition::OnParentChanged(GuiGraphicsComposition* oldParent, GuiGraphicsComposition* newParent) { GuiGraphicsSite::OnParentChanged(oldParent, newParent); stackParent = newParent == 0 ? 0 : dynamic_cast(newParent); } Size GuiStackItemComposition::GetMinSize() { return GetBoundsInternal(bounds).GetSize(); } GuiStackItemComposition::GuiStackItemComposition() :stackParent(0) { SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); } GuiStackItemComposition::~GuiStackItemComposition() { } bool GuiStackItemComposition::IsSizeAffectParent() { return false; } Rect GuiStackItemComposition::GetBounds() { Rect result = bounds; if(stackParent) { vint index = stackParent->stackItems.IndexOf(this); if (index != -1) { result = stackParent->stackItemBounds[index]; } Rect parentBounds = stackParent->previousBounds; Margin margin = stackParent->extraMargin; if (margin.left <= 0) margin.left = 0; if (margin.top <= 0) margin.top = 0; if (margin.right <= 0) margin.right = 0; if (margin.bottom <= 0) margin.bottom = 0; auto x = result.Left(); auto y = result.Top(); auto w = result.Width(); auto h = result.Height(); switch (stackParent->direction) { case GuiStackComposition::Horizontal: x += margin.left + stackParent->adjustment; y = margin.top; h = parentBounds.Height() - margin.top - margin.bottom; break; case GuiStackComposition::ReversedHorizontal: x = parentBounds.Width() - margin.right - x - w + stackParent->adjustment; y = margin.top; h = parentBounds.Height() - margin.top - margin.bottom; break; case GuiStackComposition::Vertical: x = margin.left; y += margin.top + stackParent->adjustment; w = parentBounds.Width() - margin.left - margin.right; break; case GuiStackComposition::ReversedVertical: x = margin.left; y = parentBounds.Height() - margin.bottom - y - h + stackParent->adjustment; w = parentBounds.Width() - margin.left - margin.right; break; } result = Rect( x - extraMargin.left, y - extraMargin.top, x + w + extraMargin.right, y + h + extraMargin.bottom ); } UpdatePreviousBounds(result); return result; } void GuiStackItemComposition::SetBounds(Rect value) { bounds = value; InvokeOnCompositionStateChanged(); } Margin GuiStackItemComposition::GetExtraMargin() { return extraMargin; } void GuiStackItemComposition::SetExtraMargin(Margin value) { extraMargin = value; InvokeOnCompositionStateChanged(); } } } } /*********************************************************************** .\GRAPHICSCOMPOSITION\GUIGRAPHICSTABLECOMPOSITION.CPP ***********************************************************************/ #include namespace vl { namespace presentation { namespace compositions { using namespace collections; using namespace controls; using namespace elements; /*********************************************************************** GuiTableComposition ***********************************************************************/ namespace update_cell_bounds_helpers { vint First(vint a, vint b) { return a; } vint Second(vint a, vint b) { return b; } vint X(Size s) { return s.x; } vint Y(Size s) { return s.y; } vint RL(GuiCellComposition* cell) { return cell->GetRow(); } vint CL(GuiCellComposition* cell) { return cell->GetColumn(); } vint RS(GuiCellComposition* cell) { return cell->GetRowSpan(); } vint CS(GuiCellComposition* cell) { return cell->GetColumnSpan(); } } using namespace update_cell_bounds_helpers; vint GuiTableComposition::GetSiteIndex(vint _rows, vint _columns, vint _row, vint _column) { return _row*_columns + _column; } void GuiTableComposition::SetSitedCell(vint _row, vint _column, GuiCellComposition* cell) { cellCompositions[GetSiteIndex(rows, columns, _row, _column)] = cell; } void GuiTableComposition::UpdateCellBoundsInternal( collections::Array& dimSizes, vint& dimSize, vint& dimSizeWithPercentage, collections::Array& dimOptions, vint GuiTableComposition::* dim1, vint GuiTableComposition::* dim2, vint(*getSize)(Size), vint(*getLocation)(GuiCellComposition*), vint(*getSpan)(GuiCellComposition*), vint(*getRow)(vint, vint), vint(*getCol)(vint, vint), vint maxPass ) { for (vint pass = 0; pass < maxPass; pass++) { for (vint i = 0; i < this->*dim1; i++) { GuiCellOption option = dimOptions[i]; if (pass == 0) { dimSizes[i] = 0; } switch (option.composeType) { case GuiCellOption::Absolute: { dimSizes[i] = option.absolute; } break; case GuiCellOption::MinSize: { for (vint j = 0; j < this->*dim2; j++) { GuiCellComposition* cell = GetSitedCell(getRow(i, j), getCol(i, j)); if (cell) { bool accept = false; if (pass == 0) { accept = getSpan(cell) == 1; } else { accept = getLocation(cell) + getSpan(cell) == i + 1; } if (accept) { vint size = getSize(cell->GetPreferredBounds().GetSize()); vint span = getSpan(cell); for (vint k = 1; k < span; k++) { size -= dimSizes[i - k] + cellPadding; } if (dimSizes[i] < size) { dimSizes[i] = size; } } } } } break; default:; } } } bool percentageExists = false; for (vint i = 0; i < this->*dim1; i++) { GuiCellOption option = dimOptions[i]; if (option.composeType == GuiCellOption::Percentage) { if (0.001 < option.percentage) { percentageExists = true; } } } if (percentageExists) { for (vint i = 0; i < this->*dim1; i++) { GuiCellOption option = dimOptions[i]; if (option.composeType == GuiCellOption::Percentage) { if (0.001 < option.percentage) { for (vint j = 0; j < this->*dim2; j++) { GuiCellComposition* cell = GetSitedCell(getRow(i, j), getCol(i, j)); if (cell) { vint size = getSize(cell->GetPreferredBounds().GetSize()); vint start = getLocation(cell); vint span = getSpan(cell); size -= (span - 1)*cellPadding; double totalPercentage = 0; for (vint k = start; k < start + span; k++) { if (dimOptions[k].composeType == GuiCellOption::Percentage) { if (0.001 < dimOptions[k].percentage) { totalPercentage += dimOptions[k].percentage; } } else { size -= dimSizes[k]; } } size = (vint)ceil(size*option.percentage / totalPercentage); if (dimSizes[i] < size) { dimSizes[i] = size; } } } } } } vint percentageTotalSize = 0; for (vint i = 0; i < this->*dim1; i++) { GuiCellOption option = dimOptions[i]; if (option.composeType == GuiCellOption::Percentage) { if (0.001 < option.percentage) { vint size = (vint)ceil(dimSizes[i] / option.percentage); if (percentageTotalSize < size) { percentageTotalSize = size; } } } } double totalPercentage = 0; for (vint i = 0; i < this->*dim1; i++) { GuiCellOption option = dimOptions[i]; if (option.composeType == GuiCellOption::Percentage) { if (0.001 < option.percentage) { totalPercentage += option.percentage; } } } for (vint i = 0; i < this->*dim1; i++) { GuiCellOption option = dimOptions[i]; if (option.composeType == GuiCellOption::Percentage) { if (0.001 < option.percentage) { vint size = (vint)ceil(percentageTotalSize*option.percentage / totalPercentage); if (dimSizes[i] < size) { dimSizes[i] = size; } } } } } for (vint i = 0; i < this->*dim1; i++) { if (dimOptions[i].composeType != GuiCellOption::Percentage) { dimSize += dimSizes[i]; } dimSizeWithPercentage += dimSizes[i]; } } void GuiTableComposition::UpdateCellBoundsPercentages( collections::Array& dimSizes, vint dimSize, vint maxDimSize, collections::Array& dimOptions ) { if (maxDimSize > dimSize) { double totalPercentage = 0; vint percentageCount = 0; for (vint i = 0; i < dimOptions.Count(); i++) { GuiCellOption option = dimOptions[i]; if (option.composeType == GuiCellOption::Percentage) { totalPercentage += option.percentage; percentageCount++; } } if (percentageCount > 0 && totalPercentage > 0.001) { for (vint i = 0; i < dimOptions.Count(); i++) { GuiCellOption option = dimOptions[i]; if (option.composeType == GuiCellOption::Percentage) { dimSizes[i] = (vint)((maxDimSize - dimSize)*option.percentage / totalPercentage); } } } } } vint GuiTableComposition::UpdateCellBoundsOffsets( collections::Array& offsets, collections::Array& sizes, vint max ) { offsets[0] = 0; for (vint i = 1; i < offsets.Count(); i++) { offsets[i] = offsets[i - 1] + cellPadding + sizes[i - 1]; } vint last = offsets.Count() - 1; vint right = offsets[last] + sizes[last]; return max - right; } void GuiTableComposition::OnRenderContextChanged() { if(GetRenderTarget()) { UpdateCellBounds(); } } GuiTableComposition::GuiTableComposition() :rows(0) , columns(0) , cellPadding(0) , borderVisible(true) , rowExtending(0) , columnExtending(0) { ConfigChanged.SetAssociatedComposition(this); SetRowsAndColumns(1, 1); } GuiTableComposition::~GuiTableComposition() { } vint GuiTableComposition::GetRows() { return rows; } vint GuiTableComposition::GetColumns() { return columns; } bool GuiTableComposition::SetRowsAndColumns(vint _rows, vint _columns) { if (_rows <= 0 || _columns <= 0) return false; rowOptions.Resize(_rows); columnOptions.Resize(_columns); cellCompositions.Resize(_rows*_columns); cellBounds.Resize(_rows*_columns); for (vint i = 0; i < _rows*_columns; i++) { cellCompositions[i] = 0; cellBounds[i] = Rect(); } rows = _rows; columns = _columns; vint childCount = Children().Count(); for (vint i = 0; i < childCount; i++) { GuiCellComposition* cell = dynamic_cast(Children().Get(i)); if (cell) { cell->OnTableRowsAndColumnsChanged(); } } ConfigChanged.Execute(GuiEventArgs(this)); UpdateCellBounds(); return true; } GuiCellComposition* GuiTableComposition::GetSitedCell(vint _row, vint _column) { return cellCompositions[GetSiteIndex(rows, columns, _row, _column)]; } GuiCellOption GuiTableComposition::GetRowOption(vint _row) { return rowOptions[_row]; } void GuiTableComposition::SetRowOption(vint _row, GuiCellOption option) { rowOptions[_row] = option; UpdateCellBounds(); ConfigChanged.Execute(GuiEventArgs(this)); } GuiCellOption GuiTableComposition::GetColumnOption(vint _column) { return columnOptions[_column]; } void GuiTableComposition::SetColumnOption(vint _column, GuiCellOption option) { columnOptions[_column] = option; UpdateCellBounds(); ConfigChanged.Execute(GuiEventArgs(this)); } vint GuiTableComposition::GetCellPadding() { return cellPadding; } void GuiTableComposition::SetCellPadding(vint value) { if (value < 0) value = 0; cellPadding = value; UpdateCellBounds(); } bool GuiTableComposition::GetBorderVisible() { return borderVisible; } void GuiTableComposition::SetBorderVisible(bool value) { if (borderVisible != value) { borderVisible = value; UpdateCellBounds(); } } Rect GuiTableComposition::GetCellArea() { Rect bounds(Point(0, 0), GuiBoundsComposition::GetBounds().GetSize()); vint borderThickness = borderVisible ? cellPadding : 0; bounds.x1 += margin.left + internalMargin.left + borderThickness; bounds.y1 += margin.top + internalMargin.top + borderThickness; bounds.x2 -= margin.right + internalMargin.right + borderThickness; bounds.y2 -= margin.bottom + internalMargin.bottom + borderThickness; if (bounds.x2 < bounds.x1) bounds.x2 = bounds.x1; if (bounds.y2 < bounds.y1) bounds.y2 = bounds.y1; return bounds; } void GuiTableComposition::UpdateCellBounds() { rowOffsets.Resize(rows); rowSizes.Resize(rows); columnOffsets.Resize(columns); columnSizes.Resize(columns); vint rowTotal = (rows - 1) * cellPadding; vint columnTotal = (columns - 1) * cellPadding; vint rowTotalWithPercentage = rowTotal; vint columnTotalWithPercentage = columnTotal; UpdateCellBoundsInternal( rowSizes, rowTotal, rowTotalWithPercentage, rowOptions, &GuiTableComposition::rows, &GuiTableComposition::columns, &Y, &RL, &RS, &First, &Second, 1 ); UpdateCellBoundsInternal( columnSizes, columnTotal, columnTotalWithPercentage, columnOptions, &GuiTableComposition::columns, &GuiTableComposition::rows, &X, &CL, &CS, &Second, &First, 1 ); Rect area = GetCellArea(); UpdateCellBoundsPercentages(rowSizes, rowTotal, area.Height(), rowOptions); UpdateCellBoundsPercentages(columnSizes, columnTotal, area.Width(), columnOptions); rowExtending = UpdateCellBoundsOffsets(rowOffsets, rowSizes, area.Height()); columnExtending = UpdateCellBoundsOffsets(columnOffsets, columnSizes, area.Width()); for (vint i = 0; i < rows; i++) { for (vint j = 0; j < columns; j++) { vint index = GetSiteIndex(rows, columns, i, j); cellBounds[index] = Rect(Point(columnOffsets[j], rowOffsets[i]), Size(columnSizes[j], rowSizes[i])); } } tableContentMinSize = Size(columnTotalWithPercentage, rowTotalWithPercentage); InvokeOnCompositionStateChanged(); } void GuiTableComposition::ForceCalculateSizeImmediately() { GuiBoundsComposition::ForceCalculateSizeImmediately(); UpdateCellBounds(); UpdateCellBounds(); } Size GuiTableComposition::GetMinPreferredClientSize() { vint offset = (borderVisible ? 2 * cellPadding : 0); return Size(tableContentMinSize.x + offset, tableContentMinSize.y + offset); } Rect GuiTableComposition::GetBounds() { Rect cached = previousBounds; Rect result = GuiBoundsComposition::GetBounds(); bool cellMinSizeModified = false; SortedList cells; FOREACH(GuiCellComposition*, cell, cellCompositions) { if (cell && !cells.Contains(cell)) { cells.Add(cell); Size newSize = cell->GetPreferredBounds().GetSize(); if (cell->lastPreferredSize != newSize) { cell->lastPreferredSize = newSize; cellMinSizeModified = true; } } } if (cached != result || cellMinSizeModified) { UpdateCellBounds(); } return result; } /*********************************************************************** GuiCellComposition ***********************************************************************/ void GuiCellComposition::ClearSitedCells(GuiTableComposition* table) { if (row != -1 && column != -1) { for (vint r = 0; r < rowSpan; r++) { for (vint c = 0; c < columnSpan; c++) { table->SetSitedCell(row + r, column + c, 0); } } } } void GuiCellComposition::SetSitedCells(GuiTableComposition* table) { for (vint r = 0; r < rowSpan; r++) { for (vint c = 0; c < columnSpan; c++) { table->SetSitedCell(row + r, column + c, this); } } } void GuiCellComposition::ResetSiteInternal() { row = -1; column = -1; rowSpan = 1; columnSpan = 1; } bool GuiCellComposition::SetSiteInternal(vint _row, vint _column, vint _rowSpan, vint _columnSpan) { if (tableParent) { if (_row < 0 || _row >= tableParent->rows || _column < 0 || _column >= tableParent->columns) return false; if (_rowSpan<1 || _row + _rowSpan>tableParent->rows || _columnSpan<1 || _column + _columnSpan>tableParent->columns) return false; for (vint r = 0; r < _rowSpan; r++) { for (vint c = 0; c < _columnSpan; c++) { GuiCellComposition* cell = tableParent->GetSitedCell(_row + r, _column + c); if (cell && cell != this) { return false; } } } ClearSitedCells(tableParent); } row = _row; column = _column; rowSpan = _rowSpan; columnSpan = _columnSpan; if (tableParent) { SetSitedCells(tableParent); } return true; } void GuiCellComposition::OnParentChanged(GuiGraphicsComposition* oldParent, GuiGraphicsComposition* newParent) { GuiGraphicsSite::OnParentChanged(oldParent, newParent); if (tableParent) { ClearSitedCells(tableParent); } tableParent = dynamic_cast(newParent); if (!tableParent || !SetSiteInternal(row, column, rowSpan, columnSpan)) { ResetSiteInternal(); } if (tableParent) { if (row != -1 && column != -1) { SetSiteInternal(row, column, rowSpan, columnSpan); } tableParent->UpdateCellBounds(); } } void GuiCellComposition::OnTableRowsAndColumnsChanged() { if(!SetSiteInternal(row, column, rowSpan, columnSpan)) { ResetSiteInternal(); } } GuiCellComposition::GuiCellComposition() :row(-1) ,column(-1) ,rowSpan(1) ,columnSpan(1) ,tableParent(0) { SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); } GuiCellComposition::~GuiCellComposition() { } GuiTableComposition* GuiCellComposition::GetTableParent() { return tableParent; } vint GuiCellComposition::GetRow() { return row; } vint GuiCellComposition::GetRowSpan() { return rowSpan; } vint GuiCellComposition::GetColumn() { return column; } vint GuiCellComposition::GetColumnSpan() { return columnSpan; } bool GuiCellComposition::SetSite(vint _row, vint _column, vint _rowSpan, vint _columnSpan) { if (!SetSiteInternal(_row, _column, _rowSpan, _columnSpan)) { return false; } if (tableParent) { tableParent->UpdateCellBounds(); } return true; } Rect GuiCellComposition::GetBounds() { Rect result; if(tableParent && row!=-1 && column!=-1) { Rect bounds1, bounds2; { vint index=tableParent->GetSiteIndex(tableParent->rows, tableParent->columns, row, column); bounds1=tableParent->cellBounds[index]; } { vint index=tableParent->GetSiteIndex(tableParent->rows, tableParent->columns, row+rowSpan-1, column+columnSpan-1); bounds2=tableParent->cellBounds[index]; if(tableParent->GetMinSizeLimitation()==GuiGraphicsComposition::NoLimit) { if(row+rowSpan==tableParent->rows) { bounds2.y2+=tableParent->rowExtending; } if(column+columnSpan==tableParent->columns) { bounds2.x2+=tableParent->columnExtending; } } } vint offset = tableParent->borderVisible ? tableParent->cellPadding : 0; result = Rect(bounds1.x1 + offset, bounds1.y1 + offset, bounds2.x2 + offset, bounds2.y2 + offset); } else { result = Rect(); } UpdatePreviousBounds(result); return result; } /*********************************************************************** GuiTableSplitterCompositionBase ***********************************************************************/ void GuiTableSplitterCompositionBase::OnParentChanged(GuiGraphicsComposition* oldParent, GuiGraphicsComposition* newParent) { GuiGraphicsSite::OnParentChanged(oldParent, newParent); tableParent = dynamic_cast(newParent); } void GuiTableSplitterCompositionBase::OnLeftButtonDown(GuiGraphicsComposition* sender, GuiMouseEventArgs& arguments) { dragging = true; draggingPoint = Point(arguments.x, arguments.y); } void GuiTableSplitterCompositionBase::OnLeftButtonUp(GuiGraphicsComposition* sender, GuiMouseEventArgs& arguments) { dragging = false; } void GuiTableSplitterCompositionBase::OnMouseMoveHelper( vint cellsBefore, vint GuiTableComposition::* cells, collections::Array& cellSizes, vint offset, GuiCellOption(GuiTableComposition::*getOption)(vint), void(GuiTableComposition::*setOption)(vint, GuiCellOption) ) { if (dragging) { if (tableParent) { if (0 < cellsBefore && cellsBefore < tableParent->*cells) { auto o1 = (tableParent->*getOption)(cellsBefore - 1); auto o2 = (tableParent->*getOption)(cellsBefore); vint indexStart = -1; vint indexEnd = -1; vint indexStep = -1; vint max = 0; if (offset < 0) { indexStart = cellsBefore - 1; indexEnd = -1; indexStep = -1; } else if (offset > 0) { indexStart = cellsBefore; indexEnd = tableParent->*cells; indexStep = 1; } else { return; } { auto o = (tableParent->*getOption)(indexStart); if (o.composeType == GuiCellOption::Absolute) { max = o.absolute - 1; } else { for (vint i = indexStart; i != indexEnd; i += indexStep) { o = (tableParent->*getOption)(i); if (o.composeType == GuiCellOption::Absolute) { break; } else if (o.composeType == GuiCellOption::Percentage) { max += cellSizes[i] - 1; } } } if (max <= 0) { return; } } if (offset < 0) { if (max < -offset) { offset = -max; } } else { if (max < offset) { offset = max; } } if (o1.composeType == GuiCellOption::Absolute) { o1.absolute += offset; (tableParent->*setOption)(cellsBefore - 1, o1); } if (o2.composeType == GuiCellOption::Absolute) { o2.absolute -= offset; (tableParent->*setOption)(cellsBefore, o2); } tableParent->ForceCalculateSizeImmediately(); } } } } Rect GuiTableSplitterCompositionBase::GetBoundsHelper( vint cellsBefore, vint GuiTableComposition::* cells, vint(Rect::* dimSize)()const, collections::Array& cellOffsets, vint Rect::* dimU1, vint Rect::* dimU2, vint Rect::* dimV1, vint Rect::* dimV2 ) { Rect result(0, 0, 0, 0); if (tableParent) { if (0 < cellsBefore && cellsBefore < tableParent->*cells) { vint offset = tableParent->borderVisible ? tableParent->cellPadding : 0; result.*dimU1 = offset; result.*dimU2 = offset + (tableParent->GetCellArea().*dimSize)(); result.*dimV1 = offset + cellOffsets[cellsBefore] - tableParent->cellPadding; result.*dimV2 = (result.*dimV1) + tableParent->cellPadding; } } UpdatePreviousBounds(result); return result; } GuiTableSplitterCompositionBase::GuiTableSplitterCompositionBase() :tableParent(0) , dragging(false) { SetAssociatedCursor(GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::SizeNS)); GetEventReceiver()->leftButtonDown.AttachMethod(this, &GuiTableSplitterCompositionBase::OnLeftButtonDown); GetEventReceiver()->leftButtonUp.AttachMethod(this, &GuiTableSplitterCompositionBase::OnLeftButtonUp); } GuiTableSplitterCompositionBase::~GuiTableSplitterCompositionBase() { } GuiTableComposition* GuiTableSplitterCompositionBase::GetTableParent() { return tableParent; } /*********************************************************************** GuiRowSplitterComposition ***********************************************************************/ void GuiRowSplitterComposition::OnMouseMove(GuiGraphicsComposition* sender, GuiMouseEventArgs& arguments) { OnMouseMoveHelper( rowsToTheTop, &GuiTableComposition::rows, tableParent->rowSizes, arguments.y - draggingPoint.y, &GuiTableComposition::GetRowOption, &GuiTableComposition::SetRowOption ); } GuiRowSplitterComposition::GuiRowSplitterComposition() :rowsToTheTop(0) { SetAssociatedCursor(GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::SizeNS)); GetEventReceiver()->mouseMove.AttachMethod(this, &GuiRowSplitterComposition::OnMouseMove); } GuiRowSplitterComposition::~GuiRowSplitterComposition() { } vint GuiRowSplitterComposition::GetRowsToTheTop() { return rowsToTheTop; } void GuiRowSplitterComposition::SetRowsToTheTop(vint value) { rowsToTheTop = value; InvokeOnCompositionStateChanged(); } Rect GuiRowSplitterComposition::GetBounds() { return GetBoundsHelper( rowsToTheTop, &GuiTableComposition::rows, &Rect::Width, tableParent->rowOffsets, &Rect::x1, &Rect::x2, &Rect::y1, &Rect::y2 ); } /*********************************************************************** GuiColumnSplitterComposition ***********************************************************************/ void GuiColumnSplitterComposition::OnMouseMove(GuiGraphicsComposition* sender, GuiMouseEventArgs& arguments) { OnMouseMoveHelper( columnsToTheLeft, &GuiTableComposition::columns, tableParent->columnSizes, arguments.x - draggingPoint.x, &GuiTableComposition::GetColumnOption, &GuiTableComposition::SetColumnOption ); } GuiColumnSplitterComposition::GuiColumnSplitterComposition() :columnsToTheLeft(0) { SetAssociatedCursor(GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::SizeWE)); GetEventReceiver()->mouseMove.AttachMethod(this, &GuiColumnSplitterComposition::OnMouseMove); } GuiColumnSplitterComposition::~GuiColumnSplitterComposition() { } vint GuiColumnSplitterComposition::GetColumnsToTheLeft() { return columnsToTheLeft; } void GuiColumnSplitterComposition::SetColumnsToTheLeft(vint value) { columnsToTheLeft = value; InvokeOnCompositionStateChanged(); } Rect GuiColumnSplitterComposition::GetBounds() { return GetBoundsHelper( columnsToTheLeft, &GuiTableComposition::columns, &Rect::Height, tableParent->columnOffsets, &Rect::y1, &Rect::y2, &Rect::x1, &Rect::x2 ); } } } } /*********************************************************************** .\GRAPHICSELEMENT\GUIGRAPHICSDOCUMENTELEMENT.CPP ***********************************************************************/ namespace vl { using namespace collections; namespace presentation { namespace elements { /*********************************************************************** SetPropertiesVisitor ***********************************************************************/ namespace visitors { class SetPropertiesVisitor : public Object, public DocumentRun::IVisitor { typedef GuiDocumentElement::GuiDocumentElementRenderer Renderer; typedef DocumentModel::ResolvedStyle ResolvedStyle; public: vint start; vint length; vint selectionBegin; vint selectionEnd; List styles; DocumentModel* model; Renderer* renderer; Ptr cache; IGuiGraphicsParagraph* paragraph; SetPropertiesVisitor(DocumentModel* _model, Renderer* _renderer, Ptr _cache, vint _selectionBegin, vint _selectionEnd) :start(0) ,length(0) ,model(_model) ,renderer(_renderer) ,cache(_cache) ,paragraph(_cache->graphicsParagraph.Obj()) ,selectionBegin(_selectionBegin) ,selectionEnd(_selectionEnd) { ResolvedStyle style; style=model->GetStyle(DocumentModel::DefaultStyleName, style); styles.Add(style); } void VisitContainer(DocumentContainerRun* run) { FOREACH(Ptr, subRun, run->runs) { subRun->Accept(this); } } void ApplyStyle(vint start, vint length, const ResolvedStyle& style) { paragraph->SetFont(start, length, style.style.fontFamily); paragraph->SetSize(start, length, style.style.size); paragraph->SetStyle(start, length, (IGuiGraphicsParagraph::TextStyle) ( (style.style.bold?IGuiGraphicsParagraph::Bold:0) | (style.style.italic?IGuiGraphicsParagraph::Italic:0) | (style.style.underline?IGuiGraphicsParagraph::Underline:0) | (style.style.strikeline?IGuiGraphicsParagraph::Strikeline:0) )); } void ApplyColor(vint start, vint length, const ResolvedStyle& style) { paragraph->SetColor(start, length, style.color); paragraph->SetBackgroundColor(start, length, style.backgroundColor); } void Visit(DocumentTextRun* run)override { length=run->GetRepresentationText().Length(); if(length>0) { ResolvedStyle style=styles[styles.Count()-1]; ApplyStyle(start, length, style); ApplyColor(start, length, style); vint styleStart=start; vint styleEnd=styleStart+length; if(styleStartselectionBegin?styleStart:selectionBegin; vint s3=selectionEndGetStyle(DocumentModel::SelectionStyleName, style); ApplyColor(s2, s3-s2, selectionStyle); } } } start+=length; } void Visit(DocumentStylePropertiesRun* run)override { ResolvedStyle style=styles[styles.Count()-1]; style=model->GetStyle(run->style, style); styles.Add(style); VisitContainer(run); styles.RemoveAt(styles.Count()-1); } void Visit(DocumentStyleApplicationRun* run)override { ResolvedStyle style=styles[styles.Count()-1]; style=model->GetStyle(run->styleName, style); styles.Add(style); VisitContainer(run); styles.RemoveAt(styles.Count()-1); } void Visit(DocumentHyperlinkRun* run)override { ResolvedStyle style=styles[styles.Count()-1]; style=model->GetStyle(run->styleName, style); styles.Add(style); VisitContainer(run); styles.RemoveAt(styles.Count()-1); } void Visit(DocumentImageRun* run)override { length=run->GetRepresentationText().Length(); Ptr element=GuiImageFrameElement::Create(); element->SetImage(run->image, run->frameIndex); element->SetStretch(true); IGuiGraphicsParagraph::InlineObjectProperties properties; properties.size=run->size; properties.baseline=run->baseline; properties.breakCondition=IGuiGraphicsParagraph::Alone; properties.backgroundImage = element; paragraph->SetInlineObject(start, length, properties); if(startGetStyle(DocumentModel::SelectionStyleName, style); ApplyColor(start, length, selectionStyle); } start+=length; } void Visit(DocumentEmbeddedObjectRun* run)override { length=run->GetRepresentationText().Length(); IGuiGraphicsParagraph::InlineObjectProperties properties; properties.breakCondition=IGuiGraphicsParagraph::Alone; if (run->name != L"") { vint index = renderer->nameCallbackIdMap.Keys().IndexOf(run->name); if (index != -1) { auto id = renderer->nameCallbackIdMap.Values()[index]; index = cache->embeddedObjects.Keys().IndexOf(id); if (index != -1) { auto eo = cache->embeddedObjects.Values()[index]; if (eo->start == start) { properties.size = eo->size; properties.callbackId = id; } } } else { auto eo = MakePtr(); eo->name = run->name; eo->size = Size(0, 0); eo->start = start; vint id = -1; vint count = renderer->freeCallbackIds.Count(); if (count > 0) { id = renderer->freeCallbackIds[count - 1]; renderer->freeCallbackIds.RemoveAt(count - 1); } else { id = renderer->usedCallbackIds++; } renderer->nameCallbackIdMap.Add(eo->name, id); cache->embeddedObjects.Add(id, eo); properties.callbackId = id; } } paragraph->SetInlineObject(start, length, properties); if(startGetStyle(DocumentModel::SelectionStyleName, style); ApplyColor(start, length, selectionStyle); } start+=length; } void Visit(DocumentParagraphRun* run)override { VisitContainer(run); } static vint SetProperty(DocumentModel* model, Renderer* renderer, Ptr cache, Ptr run, vint selectionBegin, vint selectionEnd) { SetPropertiesVisitor visitor(model, renderer, cache, selectionBegin, selectionEnd); run->Accept(&visitor); return visitor.length; } }; } using namespace visitors; /*********************************************************************** GuiDocumentElement::GuiDocumentElementRenderer ***********************************************************************/ Size GuiDocumentElement::GuiDocumentElementRenderer::OnRenderInlineObject(vint callbackId, Rect location) { if (element->callback) { auto cache = paragraphCaches[renderingParagraph]; auto relativeLocation = Rect(Point(location.x1 + renderingParagraphOffset.x, location.y1 + renderingParagraphOffset.y), location.GetSize()); auto eo = cache->embeddedObjects[callbackId]; auto size = element->callback->OnRenderEmbeddedObject(eo->name, relativeLocation); eo->resized = eo->size != size; eo->size = size; return eo->size; } else { return Size(); } } void GuiDocumentElement::GuiDocumentElementRenderer::InitializeInternal() { } void GuiDocumentElement::GuiDocumentElementRenderer::FinalizeInternal() { } void GuiDocumentElement::GuiDocumentElementRenderer::RenderTargetChangedInternal(IGuiGraphicsRenderTarget* oldRenderTarget, IGuiGraphicsRenderTarget* newRenderTarget) { for(vint i=0;igraphicsParagraph=0; } } } Ptr GuiDocumentElement::GuiDocumentElementRenderer::EnsureAndGetCache(vint paragraphIndex, bool createParagraph) { if(paragraphIndex<0 || paragraphIndex>=paragraphCaches.Count()) return 0; Ptr paragraph=element->document->paragraphs[paragraphIndex]; Ptr cache=paragraphCaches[paragraphIndex]; if(!cache) { cache=new ParagraphCache; cache->fullText=paragraph->GetText(false); paragraphCaches[paragraphIndex]=cache; } if(createParagraph) { if(!cache->graphicsParagraph) { cache->graphicsParagraph=layoutProvider->CreateParagraph(cache->fullText, renderTarget, this); cache->graphicsParagraph->SetParagraphAlignment(paragraph->alignment ? paragraph->alignment.Value() : Alignment::Left); SetPropertiesVisitor::SetProperty(element->document.Obj(), this, cache, paragraph, cache->selectionBegin, cache->selectionEnd); } if(cache->graphicsParagraph->GetMaxWidth()!=lastMaxWidth) { cache->graphicsParagraph->SetMaxWidth(lastMaxWidth); } vint paragraphHeight=paragraphHeights[paragraphIndex]; vint height=cache->graphicsParagraph->GetHeight(); if(paragraphHeight!=height) { cachedTotalHeight+=height-paragraphHeight; paragraphHeight=height; paragraphHeights[paragraphIndex]=paragraphHeight; minSize=Size(0, cachedTotalHeight); } } return cache; } bool GuiDocumentElement::GuiDocumentElementRenderer::GetParagraphIndexFromPoint(Point point, vint& top, vint& index) { vint y=0; for(vint i=0;iGetLayoutProvider()) ,lastCaret(-1, -1) ,lastCaretFrontSide(false) { } void GuiDocumentElement::GuiDocumentElementRenderer::Render(Rect bounds) { if (element->callback) { element->callback->OnStartRender(); } renderTarget->PushClipper(bounds); if(!renderTarget->IsClipperCoverWholeTarget()) { vint maxWidth=bounds.Width(); Rect clipper=renderTarget->GetClipper(); vint cx=bounds.Left(); vint cy=bounds.Top(); vint y1=clipper.Top()-bounds.Top(); vint y2=y1+clipper.Height(); vint y=0; lastMaxWidth=maxWidth; for(vint i=0;i=y2) { break; } else { Ptr paragraph=element->document->paragraphs[i]; Ptr cache=paragraphCaches[i]; bool created=cache && cache->graphicsParagraph; cache=EnsureAndGetCache(i, true); if(!created && i==lastCaret.row && element->caretVisible) { cache->graphicsParagraph->OpenCaret(lastCaret.column, lastCaretColor, lastCaretFrontSide); } paragraphHeight=cache->graphicsParagraph->GetHeight(); renderingParagraph = i; renderingParagraphOffset = Point(cx - bounds.x1, cy + y - bounds.y1); cache->graphicsParagraph->Render(Rect(Point(cx, cy+y), Size(maxWidth, paragraphHeight))); renderingParagraph = -1; bool resized = false; for (vint j = 0; j < cache->embeddedObjects.Count(); j++) { auto eo = cache->embeddedObjects.Values()[j]; if (eo->resized) { eo->resized = false; resized = true; } } if (resized) { cache->graphicsParagraph = 0; } } y+=paragraphHeight+paragraphDistance; } } renderTarget->PopClipper(); if (element->callback) { element->callback->OnFinishRender(); } } void GuiDocumentElement::GuiDocumentElementRenderer::OnElementStateChanged() { if (element->document && element->document->paragraphs.Count() > 0) { vint defaultSize = GetCurrentController()->ResourceService()->GetDefaultFont().size; paragraphDistance = defaultSize; vint defaultHeight = defaultSize; paragraphCaches.Resize(element->document->paragraphs.Count()); paragraphHeights.Resize(element->document->paragraphs.Count()); for (vint i = 0; i < paragraphCaches.Count(); i++) { paragraphCaches[i] = 0; } for (vint i = 0; i < paragraphHeights.Count(); i++) { paragraphHeights[i] = defaultHeight; } cachedTotalHeight = paragraphHeights.Count() * (defaultHeight + paragraphDistance); if (paragraphHeights.Count()>0) { cachedTotalHeight -= paragraphDistance; } minSize = Size(0, cachedTotalHeight); } else { paragraphCaches.Resize(0); paragraphHeights.Resize(0); cachedTotalHeight = 0; minSize = Size(0, 0); } nameCallbackIdMap.Clear(); freeCallbackIds.Clear(); usedCallbackIds = 0; } void GuiDocumentElement::GuiDocumentElementRenderer::NotifyParagraphUpdated(vint index, vint oldCount, vint newCount, bool updatedText) { if (0 <= index && index < paragraphCaches.Count() && 0 <= oldCount && index + oldCount <= paragraphCaches.Count() && 0 <= newCount) { vint paragraphCount = element->document->paragraphs.Count(); CHECK_ERROR(updatedText || oldCount == newCount, L"GuiDocumentlement::GuiDocumentElementRenderer::NotifyParagraphUpdated(vint, vint, vint, bool)#Illegal values of oldCount and newCount."); CHECK_ERROR(paragraphCount - paragraphCaches.Count() == newCount - oldCount, L"GuiDocumentElement::GuiDocumentElementRenderer::NotifyParagraphUpdated(vint, vint, vint, bool)#Illegal values of oldCount and newCount."); ParagraphCacheArray oldCaches; CopyFrom(oldCaches, paragraphCaches); paragraphCaches.Resize(paragraphCount); ParagraphHeightArray oldHeights; CopyFrom(oldHeights, paragraphHeights); paragraphHeights.Resize(paragraphCount); vint defaultHeight = GetCurrentController()->ResourceService()->GetDefaultFont().size; cachedTotalHeight = 0; for (vint i = 0; i < paragraphCount; i++) { if (i < index) { paragraphCaches[i] = oldCaches[i]; paragraphHeights[i] = oldHeights[i]; } else if (i < index + newCount) { paragraphCaches[i] = 0; paragraphHeights[i] = defaultHeight; if (!updatedText && i < index + oldCount) { auto cache = oldCaches[i]; if(cache) { cache->graphicsParagraph = 0; } paragraphCaches[i] = cache; paragraphHeights[i] = oldHeights[i]; } } else { paragraphCaches[i] = oldCaches[i - (newCount - oldCount)]; paragraphHeights[i] = oldHeights[i - (newCount - oldCount)]; } cachedTotalHeight += paragraphHeights[i] + paragraphDistance; } if (paragraphCount > 0) { cachedTotalHeight -= paragraphDistance; } if (updatedText) { vint count = oldCount < newCount ? oldCount : newCount; for (vint i = 0; i < count; i++) { if (auto cache = oldCaches[index + i]) { for (vint j = 0; j < cache->embeddedObjects.Count(); j++) { auto id = cache->embeddedObjects.Keys()[j]; auto name = cache->embeddedObjects.Values()[j]->name; nameCallbackIdMap.Remove(name); freeCallbackIds.Add(id); } } } } } } Ptr GuiDocumentElement::GuiDocumentElementRenderer::GetHyperlinkFromPoint(Point point) { vint top=0; vint index=-1; if(GetParagraphIndexFromPoint(point, top, index)) { Ptr cache=EnsureAndGetCache(index, true); Point paragraphPoint(point.x, point.y-top); vint start=-1; vint length=0; if(cache->graphicsParagraph->GetInlineObjectFromPoint(paragraphPoint, start, length)) { return element->document->GetHyperlink(index, start, start+length); } vint caret=cache->graphicsParagraph->GetCaretFromPoint(paragraphPoint); return element->document->GetHyperlink(index, caret, caret); } return 0; } void GuiDocumentElement::GuiDocumentElementRenderer::OpenCaret(TextPos caret, Color color, bool frontSide) { CloseCaret(caret); lastCaret=caret; lastCaretColor=color; lastCaretFrontSide=frontSide; Ptr cache=paragraphCaches[lastCaret.row]; if(cache && cache->graphicsParagraph) { cache->graphicsParagraph->OpenCaret(lastCaret.column, lastCaretColor, lastCaretFrontSide); } } void GuiDocumentElement::GuiDocumentElementRenderer::CloseCaret(TextPos caret) { if(lastCaret!=TextPos(-1, -1)) { if(0<=lastCaret.row && lastCaret.row cache=paragraphCaches[lastCaret.row]; if(cache && cache->graphicsParagraph) { cache->graphicsParagraph->CloseCaret(); } } } lastCaret=caret; } void GuiDocumentElement::GuiDocumentElementRenderer::SetSelection(TextPos begin, TextPos end) { if(begin>end) { TextPos t=begin; begin=end; end=t; } if(begin==end) { begin=TextPos(-1, -1); end=TextPos(-1, -1); } for(vint i=0;i cache=EnsureAndGetCache(i, false); vint newBegin=i==begin.row?begin.column:0; vint newEnd=i==end.row?end.column:cache->fullText.Length(); if(cache->selectionBegin!=newBegin || cache->selectionEnd!=newEnd) { cache->selectionBegin=newBegin; cache->selectionEnd=newEnd; NotifyParagraphUpdated(i, 1, 1, false); } } else { Ptr cache=paragraphCaches[i]; if(cache) { if(cache->selectionBegin!=-1 || cache->selectionEnd!=-1) { cache->selectionBegin=-1; cache->selectionEnd=-1; NotifyParagraphUpdated(i, 1, 1, false); } } } } } TextPos GuiDocumentElement::GuiDocumentElementRenderer::CalculateCaret(TextPos comparingCaret, IGuiGraphicsParagraph::CaretRelativePosition position, bool& preferFrontSide) { Ptr cache=EnsureAndGetCache(comparingCaret.row, true); if(cache) { switch(position) { case IGuiGraphicsParagraph::CaretFirst: { preferFrontSide=false; vint caret=cache->graphicsParagraph->GetCaret(0, IGuiGraphicsParagraph::CaretFirst, preferFrontSide); return TextPos(comparingCaret.row, caret); } case IGuiGraphicsParagraph::CaretLast: { preferFrontSide=true; vint caret=cache->graphicsParagraph->GetCaret(0, IGuiGraphicsParagraph::CaretLast, preferFrontSide); return TextPos(comparingCaret.row, caret); } case IGuiGraphicsParagraph::CaretLineFirst: { preferFrontSide=false; vint caret=cache->graphicsParagraph->GetCaret(comparingCaret.column, IGuiGraphicsParagraph::CaretLineFirst, preferFrontSide); return TextPos(comparingCaret.row, caret); } case IGuiGraphicsParagraph::CaretLineLast: { preferFrontSide=true; vint caret=cache->graphicsParagraph->GetCaret(comparingCaret.column, IGuiGraphicsParagraph::CaretLineLast, preferFrontSide); return TextPos(comparingCaret.row, caret); } case IGuiGraphicsParagraph::CaretMoveUp: { vint caret=cache->graphicsParagraph->GetCaret(comparingCaret.column, IGuiGraphicsParagraph::CaretMoveUp, preferFrontSide); if(caret==comparingCaret.column && comparingCaret.row>0) { Rect caretBounds=cache->graphicsParagraph->GetCaretBounds(comparingCaret.column, preferFrontSide); Ptr anotherCache=EnsureAndGetCache(comparingCaret.row-1, true); vint height=anotherCache->graphicsParagraph->GetHeight(); caret=anotherCache->graphicsParagraph->GetCaretFromPoint(Point(caretBounds.x1, height)); return TextPos(comparingCaret.row-1, caret); } else { return TextPos(comparingCaret.row, caret); } } case IGuiGraphicsParagraph::CaretMoveDown: { vint caret=cache->graphicsParagraph->GetCaret(comparingCaret.column, IGuiGraphicsParagraph::CaretMoveDown, preferFrontSide); if(caret==comparingCaret.column && comparingCaret.rowgraphicsParagraph->GetCaretBounds(comparingCaret.column, preferFrontSide); Ptr anotherCache=EnsureAndGetCache(comparingCaret.row+1, true); caret=anotherCache->graphicsParagraph->GetCaretFromPoint(Point(caretBounds.x1, 0)); return TextPos(comparingCaret.row+1, caret); } else { return TextPos(comparingCaret.row, caret); } } case IGuiGraphicsParagraph::CaretMoveLeft: { preferFrontSide=false; vint caret=cache->graphicsParagraph->GetCaret(comparingCaret.column, IGuiGraphicsParagraph::CaretMoveLeft, preferFrontSide); if(caret==comparingCaret.column && comparingCaret.row>0) { Ptr anotherCache=EnsureAndGetCache(comparingCaret.row-1, true); caret=anotherCache->graphicsParagraph->GetCaret(0, IGuiGraphicsParagraph::CaretLast, preferFrontSide); return TextPos(comparingCaret.row-1, caret); } else { return TextPos(comparingCaret.row, caret); } } case IGuiGraphicsParagraph::CaretMoveRight: { preferFrontSide=true; vint caret=cache->graphicsParagraph->GetCaret(comparingCaret.column, IGuiGraphicsParagraph::CaretMoveRight, preferFrontSide); if(caret==comparingCaret.column && comparingCaret.row anotherCache=EnsureAndGetCache(comparingCaret.row+1, true); caret=anotherCache->graphicsParagraph->GetCaret(0, IGuiGraphicsParagraph::CaretFirst, preferFrontSide); return TextPos(comparingCaret.row+1, caret); } else { return TextPos(comparingCaret.row, caret); } } } } return comparingCaret; } TextPos GuiDocumentElement::GuiDocumentElementRenderer::CalculateCaretFromPoint(Point point) { vint top=0; vint index=-1; if(GetParagraphIndexFromPoint(point, top, index)) { Ptr cache=EnsureAndGetCache(index, true); Point paragraphPoint(point.x, point.y-top); vint caret=cache->graphicsParagraph->GetCaretFromPoint(paragraphPoint); return TextPos(index, caret); } return TextPos(-1, -1); } Rect GuiDocumentElement::GuiDocumentElementRenderer::GetCaretBounds(TextPos caret, bool frontSide) { Ptr cache=EnsureAndGetCache(caret.row, true); if(cache) { Rect bounds=cache->graphicsParagraph->GetCaretBounds(caret.column, frontSide); if(bounds!=Rect()) { vint y=0; for(vint i=0;i(); if (elementRenderer) { elementRenderer->SetSelection(caretBegin, caretEnd); if (caretVisible) { elementRenderer->OpenCaret(caretEnd, caretColor, caretFrontSide); } else { elementRenderer->CloseCaret(caretEnd); } InvokeOnCompositionStateChanged(); } } GuiDocumentElement::GuiDocumentElement() :caretVisible(false) ,caretFrontSide(false) { } GuiDocumentElement::ICallback* GuiDocumentElement::GetCallback() { return callback; } void GuiDocumentElement::SetCallback(ICallback* value) { callback = value; } Ptr GuiDocumentElement::GetDocument() { return document; } void GuiDocumentElement::SetDocument(Ptr value) { document=value; InvokeOnElementStateChanged(); SetCaret(TextPos(), TextPos(), false); } TextPos GuiDocumentElement::GetCaretBegin() { return caretBegin; } TextPos GuiDocumentElement::GetCaretEnd() { return caretEnd; } bool GuiDocumentElement::IsCaretEndPreferFrontSide() { return caretFrontSide; } void GuiDocumentElement::SetCaret(TextPos begin, TextPos end, bool frontSide) { caretBegin=begin; caretEnd=end; if(caretBegincaretEnd) { caretFrontSide=false; } else { caretFrontSide=frontSide; } UpdateCaret(); } bool GuiDocumentElement::GetCaretVisible() { return caretVisible; } void GuiDocumentElement::SetCaretVisible(bool value) { caretVisible=value; UpdateCaret(); } Color GuiDocumentElement::GetCaretColor() { return caretColor; } void GuiDocumentElement::SetCaretColor(Color value) { caretColor=value; UpdateCaret(); } TextPos GuiDocumentElement::CalculateCaret(TextPos comparingCaret, IGuiGraphicsParagraph::CaretRelativePosition position, bool& preferFrontSide) { if (auto elementRenderer = renderer.Cast()) { TextPos caret=elementRenderer->CalculateCaret(comparingCaret, position, preferFrontSide); return caret.column==-1?comparingCaret:caret; } else { return comparingCaret; } } TextPos GuiDocumentElement::CalculateCaretFromPoint(Point point) { if (auto elementRenderer = renderer.Cast()) { return elementRenderer->CalculateCaretFromPoint(point); } else { return TextPos(0, 0); } } Rect GuiDocumentElement::GetCaretBounds(TextPos caret, bool frontSide) { if (auto elementRenderer = renderer.Cast()) { return elementRenderer->GetCaretBounds(caret, frontSide); } else { return Rect(); } } void GuiDocumentElement::NotifyParagraphUpdated(vint index, vint oldCount, vint newCount, bool updatedText) { if (auto elementRenderer = renderer.Cast()) { elementRenderer->NotifyParagraphUpdated(index, oldCount, newCount, updatedText); InvokeOnCompositionStateChanged(); } } void GuiDocumentElement::EditRun(TextPos begin, TextPos end, Ptr model, bool copy) { if (auto elementRenderer = renderer.Cast()) { if (begin > end) { TextPos temp = begin; begin = end; end = temp; } vint newRows = document->EditRun(begin, end, model, copy); if (newRows != -1) { elementRenderer->NotifyParagraphUpdated(begin.row, end.row - begin.row + 1, newRows, true); } InvokeOnCompositionStateChanged(); } } void GuiDocumentElement::EditText(TextPos begin, TextPos end, bool frontSide, const collections::Array& text) { if (auto elementRenderer = renderer.Cast()) { if (begin > end) { TextPos temp = begin; begin = end; end = temp; } vint newRows = document->EditText(begin, end, frontSide, text); if (newRows != -1) { elementRenderer->NotifyParagraphUpdated(begin.row, end.row - begin.row + 1, newRows, true); } InvokeOnCompositionStateChanged(); } } void GuiDocumentElement::EditStyle(TextPos begin, TextPos end, Ptr style) { if (auto elementRenderer = renderer.Cast()) { if (begin > end) { TextPos temp = begin; begin = end; end = temp; } if (document->EditStyle(begin, end, style)) { elementRenderer->NotifyParagraphUpdated(begin.row, end.row - begin.row + 1, end.row - begin.row + 1, false); } InvokeOnCompositionStateChanged(); } } void GuiDocumentElement::EditImage(TextPos begin, TextPos end, Ptr image) { if (auto elementRenderer = renderer.Cast()) { if (begin > end) { TextPos temp = begin; begin = end; end = temp; } if (document->EditImage(begin, end, image)) { elementRenderer->NotifyParagraphUpdated(begin.row, end.row - begin.row + 1, 1, true); } InvokeOnCompositionStateChanged(); } } void GuiDocumentElement::EditHyperlink(vint paragraphIndex, vint begin, vint end, const WString& reference, const WString& normalStyleName, const WString& activeStyleName) { if (auto elementRenderer = renderer.Cast()) { if (begin > end) { vint temp = begin; begin = end; end = temp; } if (document->EditHyperlink(paragraphIndex, begin, end, reference, normalStyleName, activeStyleName)) { elementRenderer->NotifyParagraphUpdated(paragraphIndex, 1, 1, false); } InvokeOnCompositionStateChanged(); } } void GuiDocumentElement::RemoveHyperlink(vint paragraphIndex, vint begin, vint end) { if (auto elementRenderer = renderer.Cast()) { if (begin > end) { vint temp = begin; begin = end; end = temp; } if (document->RemoveHyperlink(paragraphIndex, begin, end)) { elementRenderer->NotifyParagraphUpdated(paragraphIndex, 1, 1, false); } InvokeOnCompositionStateChanged(); } } void GuiDocumentElement::EditStyleName(TextPos begin, TextPos end, const WString& styleName) { if (auto elementRenderer = renderer.Cast()) { if (begin > end) { TextPos temp = begin; begin = end; end = temp; } if (document->EditStyleName(begin, end, styleName)) { elementRenderer->NotifyParagraphUpdated(begin.row, end.row - begin.row + 1, end.row - begin.row + 1, false); } InvokeOnCompositionStateChanged(); } } void GuiDocumentElement::RemoveStyleName(TextPos begin, TextPos end) { if (auto elementRenderer = renderer.Cast()) { if (begin > end) { TextPos temp = begin; begin = end; end = temp; } if (document->RemoveStyleName(begin, end)) { elementRenderer->NotifyParagraphUpdated(begin.row, end.row - begin.row + 1, end.row - begin.row + 1, false); } InvokeOnCompositionStateChanged(); } } void GuiDocumentElement::RenameStyle(const WString& oldStyleName, const WString& newStyleName) { if (auto elementRenderer = renderer.Cast()) { document->RenameStyle(oldStyleName, newStyleName); } } void GuiDocumentElement::ClearStyle(TextPos begin, TextPos end) { if (auto elementRenderer = renderer.Cast()) { if (begin > end) { TextPos temp = begin; begin = end; end = temp; } if (document->ClearStyle(begin, end)) { elementRenderer->NotifyParagraphUpdated(begin.row, end.row - begin.row + 1, end.row - begin.row + 1, false); } InvokeOnCompositionStateChanged(); } } Ptr GuiDocumentElement::SummarizeStyle(TextPos begin, TextPos end) { if (auto elementRenderer = renderer.Cast()) { if (begin > end) { TextPos temp = begin; begin = end; end = temp; } return document->SummarizeStyle(begin, end); } return nullptr; } Nullable GuiDocumentElement::SummarizeStyleName(TextPos begin, TextPos end) { if (auto elementRenderer = renderer.Cast()) { if (begin > end) { TextPos temp = begin; begin = end; end = temp; } return document->SummarizeStyleName(begin, end); } return {}; } void GuiDocumentElement::SetParagraphAlignment(TextPos begin, TextPos end, const collections::Array>& alignments) { if (auto elementRenderer = renderer.Cast()) { vint first = begin.row; vint last = end.row; if (first > last) { vint temp = first; first = last; last = temp; } if (0 <= first && first < document->paragraphs.Count() && 0 <= last && last < document->paragraphs.Count() && last - first + 1 == alignments.Count()) { for (vint i = first; i <= last; i++) { document->paragraphs[i]->alignment = alignments[i - first]; } elementRenderer->NotifyParagraphUpdated(first, alignments.Count(), alignments.Count(), false); } InvokeOnCompositionStateChanged(); } } Nullable GuiDocumentElement::SummarizeParagraphAlignment(TextPos begin, TextPos end) { if (auto elementRenderer = renderer.Cast()) { if (begin > end) { TextPos temp = begin; begin = end; end = temp; } return document->SummarizeParagraphAlignment(begin, end); } return {}; } Ptr GuiDocumentElement::GetHyperlinkFromPoint(Point point) { if (auto elementRenderer = renderer.Cast()) { return elementRenderer->GetHyperlinkFromPoint(point); } return nullptr; } } } } /*********************************************************************** .\GRAPHICSELEMENT\GUIGRAPHICSELEMENT.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace elements { using namespace collections; /*********************************************************************** GuiSolidBorderElement ***********************************************************************/ GuiSolidBorderElement::GuiSolidBorderElement() :color(0, 0, 0) { } Color GuiSolidBorderElement::GetColor() { return color; } void GuiSolidBorderElement::SetColor(Color value) { if(color!=value) { color=value; InvokeOnElementStateChanged(); } } ElementShape GuiSolidBorderElement::GetShape() { return shape; } void GuiSolidBorderElement::SetShape(ElementShape value) { shape=value; } /*********************************************************************** Gui3DBorderElement ***********************************************************************/ Gui3DBorderElement::Gui3DBorderElement() { } Color Gui3DBorderElement::GetColor1() { return color1; } void Gui3DBorderElement::SetColor1(Color value) { SetColors(value, color2); } Color Gui3DBorderElement::GetColor2() { return color2; } void Gui3DBorderElement::SetColor2(Color value) { SetColors(color1, value); } void Gui3DBorderElement::SetColors(Color value1, Color value2) { if(color1!=value1 || color2!=value2) { color1=value1; color2=value2; InvokeOnElementStateChanged(); } } /*********************************************************************** Gui3DSplitterElement ***********************************************************************/ Gui3DSplitterElement::Gui3DSplitterElement() :direction(Horizontal) { } Color Gui3DSplitterElement::GetColor1() { return color1; } void Gui3DSplitterElement::SetColor1(Color value) { SetColors(value, color2); } Color Gui3DSplitterElement::GetColor2() { return color2; } void Gui3DSplitterElement::SetColor2(Color value) { SetColors(color1, value); } void Gui3DSplitterElement::SetColors(Color value1, Color value2) { if(color1!=value1 || color2!=value2) { color1=value1; color2=value2; InvokeOnElementStateChanged(); } } Gui3DSplitterElement::Direction Gui3DSplitterElement::GetDirection() { return direction; } void Gui3DSplitterElement::SetDirection(Direction value) { if(direction!=value) { direction=value; InvokeOnElementStateChanged(); } } /*********************************************************************** GuiSolidBackgroundElement ***********************************************************************/ GuiSolidBackgroundElement::GuiSolidBackgroundElement() :color(255, 255, 255) { } Color GuiSolidBackgroundElement::GetColor() { return color; } void GuiSolidBackgroundElement::SetColor(Color value) { if(color!=value) { color=value; InvokeOnElementStateChanged(); } } ElementShape GuiSolidBackgroundElement::GetShape() { return shape; } void GuiSolidBackgroundElement::SetShape(ElementShape value) { shape=value; } /*********************************************************************** GuiGradientBackgroundElement ***********************************************************************/ GuiGradientBackgroundElement::GuiGradientBackgroundElement() :direction(Horizontal) { } Color GuiGradientBackgroundElement::GetColor1() { return color1; } void GuiGradientBackgroundElement::SetColor1(Color value) { SetColors(value, color2); } Color GuiGradientBackgroundElement::GetColor2() { return color2; } void GuiGradientBackgroundElement::SetColor2(Color value) { SetColors(color1, value); } void GuiGradientBackgroundElement::SetColors(Color value1, Color value2) { if(color1!=value1 || color2!=value2) { color1=value1; color2=value2; InvokeOnElementStateChanged(); } } GuiGradientBackgroundElement::Direction GuiGradientBackgroundElement::GetDirection() { return direction; } void GuiGradientBackgroundElement::SetDirection(Direction value) { if(direction!=value) { direction=value; InvokeOnElementStateChanged(); } } ElementShape GuiGradientBackgroundElement::GetShape() { return shape; } void GuiGradientBackgroundElement::SetShape(ElementShape value) { shape=value; } /*********************************************************************** GuiInnerShadowElement ***********************************************************************/ GuiInnerShadowElement::GuiInnerShadowElement() { } Color GuiInnerShadowElement::GetColor() { return color; } void GuiInnerShadowElement::SetColor(Color value) { if (color != value) { color = value; InvokeOnElementStateChanged(); } } vint GuiInnerShadowElement::GetThickness() { return thickness; } void GuiInnerShadowElement::SetThickness(vint value) { if (thickness != value) { thickness = value; InvokeOnElementStateChanged(); } } /*********************************************************************** GuiSolidLabelElement ***********************************************************************/ GuiSolidLabelElement::GuiSolidLabelElement() :color(0, 0, 0) ,hAlignment(Alignment::Left) ,vAlignment(Alignment::Top) ,wrapLine(false) ,ellipse(false) ,multiline(false) ,wrapLineHeightCalculation(false) { fontProperties.fontFamily=L"Lucida Console"; fontProperties.size=12; } Color GuiSolidLabelElement::GetColor() { return color; } void GuiSolidLabelElement::SetColor(Color value) { if(color!=value) { color=value; InvokeOnElementStateChanged(); } } const FontProperties& GuiSolidLabelElement::GetFont() { return fontProperties; } void GuiSolidLabelElement::SetFont(const FontProperties& value) { if(fontProperties!=value) { fontProperties=value; InvokeOnElementStateChanged(); } } const WString& GuiSolidLabelElement::GetText() { return text; } void GuiSolidLabelElement::SetText(const WString& value) { if(text!=value) { text=value; InvokeOnElementStateChanged(); } } Alignment GuiSolidLabelElement::GetHorizontalAlignment() { return hAlignment; } Alignment GuiSolidLabelElement::GetVerticalAlignment() { return vAlignment; } void GuiSolidLabelElement::SetHorizontalAlignment(Alignment value) { SetAlignments(value, vAlignment); } void GuiSolidLabelElement::SetVerticalAlignment(Alignment value) { SetAlignments(hAlignment, value); } void GuiSolidLabelElement::SetAlignments(Alignment horizontal, Alignment vertical) { if(hAlignment!=horizontal || vAlignment!=vertical) { hAlignment=horizontal; vAlignment=vertical; InvokeOnElementStateChanged(); } } bool GuiSolidLabelElement::GetWrapLine() { return wrapLine; } void GuiSolidLabelElement::SetWrapLine(bool value) { if(wrapLine!=value) { wrapLine=value; InvokeOnElementStateChanged(); } } bool GuiSolidLabelElement::GetEllipse() { return ellipse; } void GuiSolidLabelElement::SetEllipse(bool value) { if(ellipse!=value) { ellipse=value; InvokeOnElementStateChanged(); } } bool GuiSolidLabelElement::GetMultiline() { return multiline; } void GuiSolidLabelElement::SetMultiline(bool value) { if(multiline!=value) { multiline=value; InvokeOnElementStateChanged(); } } bool GuiSolidLabelElement::GetWrapLineHeightCalculation() { return wrapLineHeightCalculation; } void GuiSolidLabelElement::SetWrapLineHeightCalculation(bool value) { if(wrapLineHeightCalculation!=value) { wrapLineHeightCalculation=value; InvokeOnElementStateChanged(); } } /*********************************************************************** GuiImageFrameElement ***********************************************************************/ GuiImageFrameElement::GuiImageFrameElement() :frameIndex(0) ,hAlignment(Alignment::Left) ,vAlignment(Alignment::Top) ,stretch(false) ,enabled(true) { } Ptr GuiImageFrameElement::GetImage() { return image; } vint GuiImageFrameElement::GetFrameIndex() { return frameIndex; } void GuiImageFrameElement::SetImage(Ptr value) { SetImage(value, frameIndex); } void GuiImageFrameElement::SetFrameIndex(vint value) { SetImage(image, value); } void GuiImageFrameElement::SetImage(Ptr _image, vint _frameIndex) { if(image!=_image || frameIndex!=_frameIndex) { if(!_image) { image=0; frameIndex=0; } else if(0<=_frameIndex && _frameIndex<_image->GetFrameCount()) { image=_image; frameIndex=_frameIndex; } InvokeOnElementStateChanged(); } } Alignment GuiImageFrameElement::GetHorizontalAlignment() { return hAlignment; } Alignment GuiImageFrameElement::GetVerticalAlignment() { return vAlignment; } void GuiImageFrameElement::SetHorizontalAlignment(Alignment value) { SetAlignments(value, vAlignment); } void GuiImageFrameElement::SetVerticalAlignment(Alignment value) { SetAlignments(hAlignment, value); } void GuiImageFrameElement::SetAlignments(Alignment horizontal, Alignment vertical) { if(hAlignment!=horizontal || vAlignment!=vertical) { hAlignment=horizontal; vAlignment=vertical; InvokeOnElementStateChanged(); } } bool GuiImageFrameElement::GetStretch() { return stretch; } void GuiImageFrameElement::SetStretch(bool value) { if(stretch!=value) { stretch=value; InvokeOnElementStateChanged(); } } bool GuiImageFrameElement::GetEnabled() { return enabled; } void GuiImageFrameElement::SetEnabled(bool value) { if(enabled!=value) { enabled=value; InvokeOnElementStateChanged(); } } /*********************************************************************** GuiPolygonElement ***********************************************************************/ GuiPolygonElement::GuiPolygonElement() { } Size GuiPolygonElement::GetSize() { return size; } void GuiPolygonElement::SetSize(Size value) { if(size!=value) { size=value; InvokeOnElementStateChanged(); } } const Point& GuiPolygonElement::GetPoint(vint index) { return points[index]; } vint GuiPolygonElement::GetPointCount() { return points.Count(); } void GuiPolygonElement::SetPoints(const Point* p, vint count) { points.Resize(count); if(count>0) { memcpy(&points[0], p, sizeof(*p)*count); } InvokeOnElementStateChanged(); } const GuiPolygonElement::PointArray& GuiPolygonElement::GetPointsArray() { return points; } void GuiPolygonElement::SetPointsArray(const PointArray& value) { CopyFrom(points, value); InvokeOnElementStateChanged(); } Color GuiPolygonElement::GetBorderColor() { return borderColor; } void GuiPolygonElement::SetBorderColor(Color value) { if(borderColor!=value) { borderColor=value; InvokeOnElementStateChanged(); } } Color GuiPolygonElement::GetBackgroundColor() { return backgroundColor; } void GuiPolygonElement::SetBackgroundColor(Color value) { if(backgroundColor!=value) { backgroundColor=value; InvokeOnElementStateChanged(); } } } } } /*********************************************************************** .\GRAPHICSELEMENT\GUIGRAPHICSRESOURCEMANAGER.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace elements { using namespace collections; /*********************************************************************** GuiGraphicsResourceManager ***********************************************************************/ GuiGraphicsResourceManager::GuiGraphicsResourceManager() { } GuiGraphicsResourceManager::~GuiGraphicsResourceManager() { } bool GuiGraphicsResourceManager::RegisterElementFactory(IGuiGraphicsElementFactory* factory) { if(elementFactories.Keys().Contains(factory->GetElementTypeName())) { return false; } else { elementFactories.Add(factory->GetElementTypeName(), factory); return true; } } bool GuiGraphicsResourceManager::RegisterRendererFactory(const WString& elementTypeName, IGuiGraphicsRendererFactory* factory) { if(rendererFactories.Keys().Contains(elementTypeName)) { return false; } else { rendererFactories.Add(elementTypeName, factory); return true; } } IGuiGraphicsElementFactory* GuiGraphicsResourceManager::GetElementFactory(const WString& elementTypeName) { vint index=elementFactories.Keys().IndexOf(elementTypeName); return index==-1?0:elementFactories.Values().Get(index).Obj(); } IGuiGraphicsRendererFactory* GuiGraphicsResourceManager::GetRendererFactory(const WString& elementTypeName) { vint index=rendererFactories.Keys().IndexOf(elementTypeName); return index==-1?nullptr:rendererFactories.Values().Get(index).Obj(); } GuiGraphicsResourceManager* guiGraphicsResourceManager=0; GuiGraphicsResourceManager* GetGuiGraphicsResourceManager() { return guiGraphicsResourceManager; } void SetGuiGraphicsResourceManager(GuiGraphicsResourceManager* resourceManager) { guiGraphicsResourceManager=resourceManager; } bool RegisterFactories(IGuiGraphicsElementFactory* elementFactory, IGuiGraphicsRendererFactory* rendererFactory) { if(guiGraphicsResourceManager && elementFactory && rendererFactory) { if(guiGraphicsResourceManager->RegisterElementFactory(elementFactory)) { if(guiGraphicsResourceManager->RegisterRendererFactory(elementFactory->GetElementTypeName(), rendererFactory)) { return true; } } } return false; } } } } /*********************************************************************** .\GRAPHICSELEMENT\GUIGRAPHICSTEXTELEMENT.CPP ***********************************************************************/ namespace vl { using namespace collections; namespace presentation { namespace elements { namespace text { /*********************************************************************** text::TextLine ***********************************************************************/ TextLine::TextLine() :text(0) ,att(0) ,availableOffsetCount(0) ,bufferLength(0) ,dataLength(0) ,lexerFinalState(-1) ,contextFinalState(-1) { } TextLine::~TextLine() { } vint TextLine::CalculateBufferLength(vint dataLength) { if(dataLength<1)dataLength=1; vint bufferLength=dataLength-dataLength%BlockSize; if(bufferLengthdataLength || inputCount<0) return false; vint newDataLength=dataLength-count+inputCount; vint newBufferLength=CalculateBufferLength(newDataLength); if(newBufferLength!=bufferLength) { wchar_t* newText=new wchar_t[newBufferLength]; memcpy(newText, text, start*sizeof(wchar_t)); memcpy(newText+start, input, inputCount*sizeof(wchar_t)); memcpy(newText+start+inputCount, text+start+count, (dataLength-start-count)*sizeof(wchar_t)); CharAtt* newAtt=new CharAtt[newBufferLength]; memcpy(newAtt, att, start*sizeof(CharAtt)); memset(newAtt+start, 0, inputCount*sizeof(CharAtt)); memcpy(newAtt+start+inputCount, att+start+count, (dataLength-start-count)*sizeof(CharAtt)); delete[] text; delete[] att; text=newText; att=newAtt; } else { memmove(text+start+inputCount, text+start+count, (dataLength-start-count)*sizeof(wchar_t)); memmove(att+start+inputCount, att+start+count, (dataLength-start-count)*sizeof(CharAtt)); memcpy(text+start, input, inputCount*sizeof(wchar_t)); memset(att+start, 0, inputCount*sizeof(CharAtt)); } dataLength=newDataLength; bufferLength=newBufferLength; if(availableOffsetCount>start) { availableOffsetCount=start; } return true; } TextLine TextLine::Split(vint index) { if(index<0 || index>dataLength) return TextLine(); vint count=dataLength-index; TextLine line; line.Initialize(); line.Modify(0, 0, text+index, count); memcpy(line.att, att+index, count*sizeof(CharAtt)); Modify(index, count, L"", 0); return line; } void TextLine::AppendAndFinalize(TextLine& line) { vint oldDataLength=dataLength; Modify(oldDataLength, 0, line.text, line.dataLength); memcpy(att+oldDataLength, line.att, line.dataLength*sizeof(CharAtt)); line.Finalize(); } /*********************************************************************** text::CharMeasurer ***********************************************************************/ CharMeasurer::CharMeasurer(vint _rowHeight) :oldRenderTarget(0) ,rowHeight(_rowHeight) { memset(widths, 0, sizeof(widths)); } CharMeasurer::~CharMeasurer() { } void CharMeasurer::SetRenderTarget(IGuiGraphicsRenderTarget* value) { if(oldRenderTarget!=value) { oldRenderTarget=value; rowHeight=GetRowHeightInternal(oldRenderTarget); memset(widths, 0, sizeof(widths)); } } vint CharMeasurer::MeasureWidth(wchar_t character) { vint w=widths[character]; if(w==0) { widths[character]=w=MeasureWidthInternal(character, oldRenderTarget); } return w; } vint CharMeasurer::GetRowHeight() { return rowHeight; } /*********************************************************************** text::TextLines ***********************************************************************/ TextLines::TextLines(GuiColorizedTextElement* _ownerElement) :ownerElement(_ownerElement) ,charMeasurer(0) ,renderTarget(0) ,tabWidth(1) ,tabSpaceCount(4) ,passwordChar(L'\0') { TextLine line; line.Initialize(); lines.Add(line); } TextLines::~TextLines() { RemoveLines(0, lines.Count()); } //-------------------------------------------------------- vint TextLines::GetCount() { return lines.Count(); } TextLine& TextLines::GetLine(vint row) { return lines[row]; } CharMeasurer* TextLines::GetCharMeasurer() { return charMeasurer; } void TextLines::SetCharMeasurer(CharMeasurer* value) { charMeasurer=value; if(charMeasurer) charMeasurer->SetRenderTarget(renderTarget); ClearMeasurement(); } IGuiGraphicsRenderTarget* TextLines::GetRenderTarget() { return renderTarget; } void TextLines::SetRenderTarget(IGuiGraphicsRenderTarget* value) { renderTarget=value; if(charMeasurer) charMeasurer->SetRenderTarget(renderTarget); ClearMeasurement(); } WString TextLines::GetText(TextPos start, TextPos end) { if(!IsAvailable(start) || !IsAvailable(end) || start>end) return L""; if(start.row==end.row) { return WString(lines[start.row].text+start.column, end.column-start.column); } vint count=0; for(vint i=start.row+1;i buffer; buffer.Resize(count+(end.row-start.row)*2); wchar_t* writing=&buffer[0]; for(vint i=start.row;i<=end.row;i++) { wchar_t* text=lines[i].text; vint chars=0; if(i==start.row) { text+=start.column; chars=lines[i].dataLength-start.column; } else if(i==end.row) { chars=end.column; } else { chars=lines[i].dataLength; } if(i!=start.row) { *writing++=L'\r'; *writing++=L'\n'; } memcpy(writing, text, chars*sizeof(wchar_t)); writing+=chars; } return WString(&buffer[0], buffer.Count()); } WString TextLines::GetText() { return GetText(TextPos(0, 0), TextPos(lines.Count()-1, lines[lines.Count()-1].dataLength)); } void TextLines::SetText(const WString& value) { Modify(TextPos(0, 0), TextPos(lines.Count()-1, lines[lines.Count()-1].dataLength), value); } //-------------------------------------------------------- bool TextLines::RemoveLines(vint start, vint count) { if(start<0 || count<0 || start+count>lines.Count()) return false; for(vint i=start+count-1;i>=start;i--) { lines[i].Finalize(); } lines.RemoveRange(start, count); return true; } bool TextLines::IsAvailable(TextPos pos) { return 0<=pos.row && pos.row=lines.Count()) { return TextPos(lines.Count()-1, lines[lines.Count()-1].dataLength); } else { TextLine& line=lines[pos.row]; if(pos.column<0) { return TextPos(pos.row, 0); } else if(pos.column>line.dataLength) { return TextPos(pos.row, line.dataLength); } else { return pos; } } } TextPos TextLines::Modify(TextPos start, TextPos end, const wchar_t** inputs, vint* inputCounts, vint rows) { if(!IsAvailable(start) || !IsAvailable(end) || start>end) return TextPos(-1, -1); if (ownerElement) { ownerElement->InvokeOnElementStateChanged(); } if(rows==1) { if(start.row==end.row) { lines[start.row].Modify(start.column, end.column-start.column, inputs[0], inputCounts[0]); } else { if(end.row-start.row>1) { RemoveLines(start.row+1, end.row-start.row-1); } vint modifyCount=lines[start.row].dataLength-start.column+end.column; lines[start.row].AppendAndFinalize(lines[start.row+1]); lines.RemoveAt(start.row+1); lines[start.row].Modify(start.column, modifyCount, inputs[0], inputCounts[0]); } return TextPos(start.row, start.column+inputCounts[0]); } if(start.row==end.row) { TextLine newLine=lines[start.row].Split(end.column); lines.Insert(start.row+1, newLine); end=TextPos(start.row+1, 0); } vint oldMiddleLines=end.row-start.row-1; vint newMiddleLines=rows-2; if(oldMiddleLinesnewMiddleLines) { RemoveLines(start.row+newMiddleLines+1, oldMiddleLines-newMiddleLines); } end.row+=newMiddleLines-oldMiddleLines; lines[start.row].Modify(start.column, lines[start.row].dataLength-start.column, inputs[0], inputCounts[0]); lines[end.row].Modify(0, end.column, inputs[rows-1], inputCounts[rows-1]); for(vint i=1;i inputs; List inputCounts; const wchar_t* previous=input; const wchar_t* current=input; while(true) { if(current==input+inputCount) { inputs.Add(previous); inputCounts.Add(current-previous); break; } else if(*current==L'\r' || *current==L'\n') { inputs.Add(previous); inputCounts.Add(current-previous); previous=current+(current[1]==L'\n'?2:1); current=previous; } else { current++; } } return Modify(start, end, &inputs[0], &inputCounts[0], inputs.Count()); } TextPos TextLines::Modify(TextPos start, TextPos end, const wchar_t* input) { return Modify(start, end, input, wcslen(input)); } TextPos TextLines::Modify(TextPos start, TextPos end, const WString& input) { return Modify(start, end, input.Buffer(), input.Length()); } void TextLines::Clear() { RemoveLines(0, lines.Count()); TextLine line; line.Initialize(); lines.Add(line); if (ownerElement) { ownerElement->InvokeOnElementStateChanged(); } } //-------------------------------------------------------- void TextLines::ClearMeasurement() { for (vint i = 0; i < lines.Count(); i++) { lines[i].availableOffsetCount = 0; } tabWidth = tabSpaceCount * (charMeasurer ? charMeasurer->MeasureWidth(L' ') : 1); if (tabWidth == 0) { tabWidth = 1; } if (ownerElement) { ownerElement->InvokeOnElementStateChanged(); } } vint TextLines::GetTabSpaceCount() { return tabSpaceCount; } void TextLines::SetTabSpaceCount(vint value) { if(value<1) value=1; if(tabSpaceCount!=value) { tabSpaceCount=value; ClearMeasurement(); } } void TextLines::MeasureRow(vint row) { TextLine& line=lines[row]; vint offset=0; if(line.availableOffsetCount) { offset=line.att[line.availableOffsetCount-1].rightOffset; } for(vint i=line.availableOffsetCount;iMeasureWidth(passwordChar) : 1; } else if(c==L'\t') { width=tabWidth-offset%tabWidth; } else { width = charMeasurer ? charMeasurer->MeasureWidth(line.text[i]) : 1; } offset+=width; att.rightOffset=(int)offset; } line.availableOffsetCount=line.dataLength; } vint TextLines::GetRowWidth(vint row) { if(row<0 || row>=lines.Count()) return -1; TextLine& line=lines[row]; if(line.dataLength==0) { return 0; } else { MeasureRow(row); return line.att[line.dataLength-1].rightOffset; } } vint TextLines::GetRowHeight() { return charMeasurer ? charMeasurer->GetRowHeight() : 1; } vint TextLines::GetMaxWidth() { vint width=0; for(vint i=0;i=h*lines.Count()) { point.y=h*lines.Count()-1; } vint row=point.y/h; if(point.x<0) { return TextPos(row, 0); } else if(point.x>=GetRowWidth(row)) { return TextPos(row, lines[row].dataLength); } TextLine& line=lines[row]; vint i1=0, i2=line.dataLength; vint p1=0, p2=line.att[line.dataLength-1].rightOffset; while(i2-i1>1) { vint i=(i1+i2)/2; vint p=i==0?0:line.att[i-1].rightOffset; if(point.xColorChanged(); InvokeOnElementStateChanged(); } void GuiColorizedTextElement::ResetTextColorIndex(vint index) { vint lineCount = lines.GetCount(); for (vint i = 0; i < lineCount; i++) { auto& line = lines.GetLine(i); line.lexerFinalState = -1; line.contextFinalState = -1; for (vint j = 0; j < line.dataLength; j++) { line.att[j].colorIndex = (vuint32_t)index; } } } const FontProperties& GuiColorizedTextElement::GetFont() { return font; } void GuiColorizedTextElement::SetFont(const FontProperties& value) { if(font!=value) { font=value; if(callback) { callback->FontChanged(); } InvokeOnElementStateChanged(); } } wchar_t GuiColorizedTextElement::GetPasswordChar() { return lines.GetPasswordChar(); } void GuiColorizedTextElement::SetPasswordChar(wchar_t value) { if(lines.GetPasswordChar()!=value) { lines.SetPasswordChar(value); InvokeOnElementStateChanged(); } } Point GuiColorizedTextElement::GetViewPosition() { return viewPosition; } void GuiColorizedTextElement::SetViewPosition(Point value) { if(viewPosition!=value) { viewPosition=value; InvokeOnElementStateChanged(); } } bool GuiColorizedTextElement::GetVisuallyEnabled() { return isVisuallyEnabled; } void GuiColorizedTextElement::SetVisuallyEnabled(bool value) { if(isVisuallyEnabled!=value) { isVisuallyEnabled=value; InvokeOnElementStateChanged(); } } bool GuiColorizedTextElement::GetFocused() { return isFocused; } void GuiColorizedTextElement::SetFocused(bool value) { if(isFocused!=value) { isFocused=value; InvokeOnElementStateChanged(); } } TextPos GuiColorizedTextElement::GetCaretBegin() { return caretBegin; } void GuiColorizedTextElement::SetCaretBegin(TextPos value) { caretBegin=value; InvokeOnElementStateChanged(); } TextPos GuiColorizedTextElement::GetCaretEnd() { return caretEnd; } void GuiColorizedTextElement::SetCaretEnd(TextPos value) { caretEnd=value; InvokeOnElementStateChanged(); } bool GuiColorizedTextElement::GetCaretVisible() { return caretVisible; } void GuiColorizedTextElement::SetCaretVisible(bool value) { caretVisible=value; InvokeOnElementStateChanged(); } Color GuiColorizedTextElement::GetCaretColor() { return caretColor; } void GuiColorizedTextElement::SetCaretColor(Color value) { if(caretColor!=value) { caretColor=value; InvokeOnElementStateChanged(); } } } } } /*********************************************************************** .\NATIVEWINDOW\GUINATIVEWINDOW.CPP ***********************************************************************/ namespace vl { namespace presentation { /*********************************************************************** INativeWindowListener ***********************************************************************/ INativeWindowListener::HitTestResult INativeWindowListener::HitTest(Point location) { return INativeWindowListener::NoDecision; } void INativeWindowListener::Moving(Rect& bounds, bool fixSizeOnly) { } void INativeWindowListener::Moved() { } void INativeWindowListener::Enabled() { } void INativeWindowListener::Disabled() { } void INativeWindowListener::GotFocus() { } void INativeWindowListener::LostFocus() { } void INativeWindowListener::Activated() { } void INativeWindowListener::Deactivated() { } void INativeWindowListener::Opened() { } void INativeWindowListener::Closing(bool& cancel) { } void INativeWindowListener::Closed() { } void INativeWindowListener::Paint() { } void INativeWindowListener::Destroying() { } void INativeWindowListener::Destroyed() { } void INativeWindowListener::LeftButtonDown(const NativeWindowMouseInfo& info) { } void INativeWindowListener::LeftButtonUp(const NativeWindowMouseInfo& info) { } void INativeWindowListener::LeftButtonDoubleClick(const NativeWindowMouseInfo& info) { } void INativeWindowListener::RightButtonDown(const NativeWindowMouseInfo& info) { } void INativeWindowListener::RightButtonUp(const NativeWindowMouseInfo& info) { } void INativeWindowListener::RightButtonDoubleClick(const NativeWindowMouseInfo& info) { } void INativeWindowListener::MiddleButtonDown(const NativeWindowMouseInfo& info) { } void INativeWindowListener::MiddleButtonUp(const NativeWindowMouseInfo& info) { } void INativeWindowListener::MiddleButtonDoubleClick(const NativeWindowMouseInfo& info) { } void INativeWindowListener::HorizontalWheel(const NativeWindowMouseInfo& info) { } void INativeWindowListener::VerticalWheel(const NativeWindowMouseInfo& info) { } void INativeWindowListener::MouseMoving(const NativeWindowMouseInfo& info) { } void INativeWindowListener::MouseEntered() { } void INativeWindowListener::MouseLeaved() { } void INativeWindowListener::KeyDown(const NativeWindowKeyInfo& info) { } void INativeWindowListener::KeyUp(const NativeWindowKeyInfo& info) { } void INativeWindowListener::SysKeyDown(const NativeWindowKeyInfo& info) { } void INativeWindowListener::SysKeyUp(const NativeWindowKeyInfo& info) { } void INativeWindowListener::Char(const NativeWindowCharInfo& info) { } /*********************************************************************** INativeControllerListener ***********************************************************************/ void INativeControllerListener::LeftButtonDown(Point position) { } void INativeControllerListener::LeftButtonUp(Point position) { } void INativeControllerListener::RightButtonDown(Point position) { } void INativeControllerListener::RightButtonUp(Point position) { } void INativeControllerListener::MouseMoving(Point position) { } void INativeControllerListener::GlobalTimer() { } void INativeControllerListener::ClipboardUpdated() { } void INativeControllerListener::NativeWindowCreated(INativeWindow* window) { } void INativeControllerListener::NativeWindowDestroying(INativeWindow* window) { } /*********************************************************************** Native Window Provider ***********************************************************************/ INativeController* currentController=0; INativeController* GetCurrentController() { return currentController; } void SetCurrentController(INativeController* controller) { currentController=controller; } } } /*********************************************************************** .\RESOURCES\GUIDOCUMENT.CPP ***********************************************************************/ namespace vl { namespace presentation { using namespace collections; using namespace parsing::tabling; using namespace parsing::xml; using namespace regex; /*********************************************************************** DocumentFontSize ***********************************************************************/ DocumentFontSize DocumentFontSize::Parse(const WString& value) { if (value.Length() > 0 && value[value.Length() - 1] == L'x') { return DocumentFontSize(wtof(value.Left(value.Length() - 1)), true); } else { return DocumentFontSize(wtof(value), false); } } WString DocumentFontSize::ToString()const { return ftow(size) + (relative ? L"x" : L""); } /*********************************************************************** DocumentImageRun ***********************************************************************/ const wchar_t* DocumentImageRun::RepresentationText=L"[Image]"; const wchar_t* DocumentEmbeddedObjectRun::RepresentationText=L"[EmbeddedObject]"; /*********************************************************************** ExtractTextVisitor ***********************************************************************/ namespace document_operation_visitors { class ExtractTextVisitor : public Object, public DocumentRun::IVisitor { public: stream::TextWriter& writer; bool skipNonTextContent; ExtractTextVisitor(stream::TextWriter& _writer, bool _skipNonTextContent) :writer(_writer) ,skipNonTextContent(_skipNonTextContent) { } void VisitContainer(DocumentContainerRun* run) { FOREACH(Ptr, subRun, run->runs) { subRun->Accept(this); } } void VisitContent(DocumentContentRun* run) { writer.WriteString(run->GetRepresentationText()); } void Visit(DocumentTextRun* run)override { VisitContent(run); } void Visit(DocumentStylePropertiesRun* run)override { VisitContainer(run); } void Visit(DocumentStyleApplicationRun* run)override { VisitContainer(run); } void Visit(DocumentHyperlinkRun* run)override { VisitContainer(run); } void Visit(DocumentImageRun* run)override { if(!skipNonTextContent) { VisitContent(run); } } void Visit(DocumentEmbeddedObjectRun* run)override { if(!skipNonTextContent) { VisitContent(run); } } void Visit(DocumentParagraphRun* run)override { VisitContainer(run); } }; } using namespace document_operation_visitors; /*********************************************************************** DocumentParagraphRun ***********************************************************************/ WString DocumentParagraphRun::GetText(bool skipNonTextContent) { stream::MemoryStream memoryStream; { stream::StreamWriter writer(memoryStream); GetText(writer, skipNonTextContent); } memoryStream.SeekFromBegin(0); stream::StreamReader reader(memoryStream); return reader.ReadToEnd(); } void DocumentParagraphRun::GetText(stream::TextWriter& writer, bool skipNonTextContent) { ExtractTextVisitor visitor(writer, skipNonTextContent); Accept(&visitor); } /*********************************************************************** DocumentModel ***********************************************************************/ const wchar_t* DocumentModel::DefaultStyleName = L"#Default"; const wchar_t* DocumentModel::SelectionStyleName = L"#Selection"; const wchar_t* DocumentModel::ContextStyleName = L"#Context"; const wchar_t* DocumentModel::NormalLinkStyleName = L"#NormalLink"; const wchar_t* DocumentModel::ActiveLinkStyleName = L"#ActiveLink"; DocumentModel::DocumentModel() { { FontProperties font=GetCurrentController()->ResourceService()->GetDefaultFont(); Ptr sp=new DocumentStyleProperties; sp->face=font.fontFamily; sp->size=DocumentFontSize((double)font.size, false); sp->color=Color(); sp->backgroundColor=Color(0, 0, 0, 0); sp->bold=font.bold; sp->italic=font.italic; sp->underline=font.underline; sp->strikeline=font.strikeline; sp->antialias=font.antialias; sp->verticalAntialias=font.verticalAntialias; Ptr style=new DocumentStyle; style->styles=sp; styles.Add(L"#Default", style); } { Ptr sp=new DocumentStyleProperties; sp->color=Color(255, 255, 255); sp->backgroundColor=Color(51, 153, 255); Ptr style=new DocumentStyle; style->styles=sp; styles.Add(L"#Selection", style); } { Ptr sp=new DocumentStyleProperties; Ptr style=new DocumentStyle; style->styles=sp; styles.Add(L"#Context", style); } { Ptr sp=new DocumentStyleProperties; sp->color=Color(0, 0, 255); sp->underline=true; Ptr style=new DocumentStyle; style->parentStyleName=L"#Context"; style->styles=sp; styles.Add(L"#NormalLink", style); } { Ptr sp=new DocumentStyleProperties; sp->color=Color(255, 128, 0); sp->underline=true; Ptr style=new DocumentStyle; style->parentStyleName=L"#Context"; style->styles=sp; styles.Add(L"#ActiveLink", style); } } void DocumentModel::MergeStyle(Ptr style, Ptr parent) { if(!style->face && parent->face) style->face =parent->face; if(!style->size && parent->size) style->size =parent->size; if(!style->color && parent->color) style->color =parent->color; if(!style->backgroundColor && parent->backgroundColor) style->backgroundColor =parent->backgroundColor; if(!style->bold && parent->bold) style->bold =parent->bold; if(!style->italic && parent->italic) style->italic =parent->italic; if(!style->underline && parent->underline) style->underline =parent->underline; if(!style->strikeline && parent->strikeline) style->strikeline =parent->strikeline; if(!style->antialias && parent->antialias) style->antialias =parent->antialias; if(!style->verticalAntialias && parent->verticalAntialias) style->verticalAntialias =parent->verticalAntialias; } void DocumentModel::MergeBaselineStyle(Ptr style, const WString& styleName) { auto indexDst = styles.Keys().IndexOf(styleName); Ptr sp = new DocumentStyleProperties; MergeStyle(sp, style); if (indexDst != -1) { MergeStyle(sp, styles.Values()[indexDst]->styles); } if (indexDst == -1) { auto style = new DocumentStyle; style->styles = sp; styles.Add(styleName, style); } else { styles.Values()[indexDst]->styles = sp; } FOREACH(Ptr, style, styles.Values()) { style->resolvedStyles = nullptr; } } void DocumentModel::MergeBaselineStyle(Ptr baselineDocument, const WString& styleName) { auto indexSrc = baselineDocument->styles.Keys().IndexOf(styleName + L"-Override"); if (indexSrc == -1) { return; } auto csp = baselineDocument->styles.Values()[indexSrc]->styles; MergeBaselineStyle(csp, styleName); } void DocumentModel::MergeBaselineStyles(Ptr baselineDocument) { MergeBaselineStyle(baselineDocument, DefaultStyleName); MergeBaselineStyle(baselineDocument, SelectionStyleName); MergeBaselineStyle(baselineDocument, ContextStyleName); MergeBaselineStyle(baselineDocument, NormalLinkStyleName); MergeBaselineStyle(baselineDocument, ActiveLinkStyleName); } void DocumentModel::MergeDefaultFont(const FontProperties& defaultFont) { Ptr style = new DocumentStyleProperties; style->face =defaultFont.fontFamily; style->size =DocumentFontSize((double)defaultFont.size, false); style->bold =defaultFont.bold; style->italic =defaultFont.italic; style->underline =defaultFont.underline; style->strikeline =defaultFont.strikeline; style->antialias =defaultFont.antialias; style->verticalAntialias =defaultFont.verticalAntialias; MergeBaselineStyle(style, DefaultStyleName); } DocumentModel::ResolvedStyle DocumentModel::GetStyle(Ptr sp, const ResolvedStyle& context) { FontProperties font; font.fontFamily =sp->face ?sp->face.Value() :context.style.fontFamily; font.bold =sp->bold ?sp->bold.Value() :context.style.bold; font.italic =sp->italic ?sp->italic.Value() :context.style.italic; font.underline =sp->underline ?sp->underline.Value() :context.style.underline; font.strikeline =sp->strikeline ?sp->strikeline.Value() :context.style.strikeline; font.antialias =sp->antialias ?sp->antialias.Value() :context.style.antialias; font.verticalAntialias =sp->verticalAntialias ?sp->verticalAntialias.Value() :context.style.verticalAntialias; Color color =sp->color ?sp->color.Value() :context.color; Color backgroundColor =sp->backgroundColor ?sp->backgroundColor.Value() :context.backgroundColor; if (sp->size) { font.size = (vint)(sp->size.Value().relative ? context.style.size * sp->size.Value().size : sp->size.Value().size); } else { font.size = context.style.size; } return ResolvedStyle(font, color, backgroundColor); } DocumentModel::ResolvedStyle DocumentModel::GetStyle(const WString& styleName, const ResolvedStyle& context) { Ptr selectedStyle; { vint index=styles.Keys().IndexOf(styleName); if(index!=-1) { selectedStyle=styles.Values()[index]; } else { selectedStyle=styles[L"#Default"]; } } if(!selectedStyle->resolvedStyles) { Ptr sp = new DocumentStyleProperties; selectedStyle->resolvedStyles = sp; Ptr currentStyle; WString currentName = styleName; while(true) { vint index = styles.Keys().IndexOf(currentName); if (index == -1) break; currentStyle = styles.Values().Get(index); currentName = currentStyle->parentStyleName; MergeStyle(sp, currentStyle->styles); } } Ptr sp=selectedStyle->resolvedStyles; return GetStyle(sp, context); } WString DocumentModel::GetText(bool skipNonTextContent) { stream::MemoryStream memoryStream; { stream::StreamWriter writer(memoryStream); GetText(writer, skipNonTextContent); } memoryStream.SeekFromBegin(0); stream::StreamReader reader(memoryStream); return reader.ReadToEnd(); } void DocumentModel::GetText(stream::TextWriter& writer, bool skipNonTextContent) { for(vint i=0;i paragraph=paragraphs[i]; paragraph->GetText(writer, skipNonTextContent); if(i parent; public: SerializeRunVisitor(DocumentModel* _model, Ptr _parent) :model(_model) , parent(_parent) { } void VisitContainer(Ptr replacedParent, DocumentContainerRun* run) { if (replacedParent) { parent->subNodes.Add(replacedParent); Ptr oldParent = parent; parent = replacedParent; FOREACH(Ptr, subRun, run->runs) { subRun->Accept(this); } parent = oldParent; } else { FOREACH(Ptr, subRun, run->runs) { subRun->Accept(this); } } } void Visit(DocumentTextRun* run)override { if (run->text != L"") { auto writer = XmlElementWriter(parent).Element(L"nop"); auto begin = run->text.Buffer(); auto reading = begin; auto last = reading; while (true) { const wchar_t* tag = nullptr; auto c = *reading; switch (c) { case L'\n': tag = L"br"; break; case L' ': tag = L"sp"; break; case L'\t': tag = L"tab"; break; } if (tag || c == 0) { if (reading > last) { auto end = reading[-1] == L'\r' ? reading - 1 : reading; if (end > last) { writer.Text(run->text.Sub(last - begin, end - last)); } last = reading; } } if (tag) { writer.Element(tag); } else if (c == 0) { break; } reading++; } } } void Visit(DocumentStylePropertiesRun* run)override { Ptr sp = run->style; Ptr oldParent = parent; if (sp->face || sp->size || sp->color) { Ptr element = new XmlElement; element->name.value = L"font"; parent->subNodes.Add(element); XmlElementWriter writer(element); if (sp->face) { writer.Attribute(L"face", sp->face.Value()); } if (sp->size) { writer.Attribute(L"size", sp->size.Value().ToString()); } if (sp->color) { writer.Attribute(L"color", sp->color.Value().ToString()); } if (sp->backgroundColor) { writer.Attribute(L"bkcolor", sp->backgroundColor.Value().ToString()); } parent = element; } if (sp->bold) { Ptr element = new XmlElement; element->name.value = sp->bold.Value() ? L"b" : L"b-"; parent->subNodes.Add(element); parent = element; } if (sp->italic) { Ptr element = new XmlElement; element->name.value = sp->italic.Value() ? L"i" : L"i-"; parent->subNodes.Add(element); parent = element; } if (sp->underline) { Ptr element = new XmlElement; element->name.value = sp->underline.Value() ? L"u" : L"u-"; parent->subNodes.Add(element); parent = element; } if (sp->strikeline) { Ptr element = new XmlElement; element->name.value = sp->strikeline.Value() ? L"s" : L"s-"; parent->subNodes.Add(element); parent = element; } if (sp->antialias || sp->verticalAntialias) { bool ha = sp->antialias ? sp->antialias.Value() : true; bool va = sp->verticalAntialias ? sp->verticalAntialias.Value() : false; if (!ha) { Ptr element = new XmlElement; element->name.value = L"ha"; parent->subNodes.Add(element); parent = element; } else if (!va) { Ptr element = new XmlElement; element->name.value = L"va"; parent->subNodes.Add(element); parent = element; } else { Ptr element = new XmlElement; element->name.value = L"na"; parent->subNodes.Add(element); parent = element; } } VisitContainer(0, run); parent = oldParent; } void Visit(DocumentStyleApplicationRun* run)override { Ptr element = new XmlElement; element->name.value = L"div"; XmlElementWriter(element).Attribute(L"style", run->styleName); VisitContainer(element, run); } void Visit(DocumentHyperlinkRun* run)override { Ptr element = new XmlElement; element->name.value = L"a"; XmlElementWriter writer(element); if (run->normalStyleName != L"#NormalLink") { writer.Attribute(L"normal", run->normalStyleName); } if (run->activeStyleName != L"#ActiveLink") { writer.Attribute(L"active", run->activeStyleName); } if (run->reference != L"") { writer.Attribute(L"href", run->reference); } VisitContainer(element, run); } void Visit(DocumentImageRun* run)override { XmlElementWriter writer(parent); writer .Element(L"img") .Attribute(L"width", itow(run->size.x)) .Attribute(L"height", itow(run->size.y)) .Attribute(L"baseline", itow(run->baseline)) .Attribute(L"frameIndex", itow(run->frameIndex)) .Attribute(L"source", run->source) ; } void Visit(DocumentEmbeddedObjectRun* run)override { XmlElementWriter writer(parent); writer .Element(L"object") .Attribute(L"name", run->name) ; } void Visit(DocumentParagraphRun* run)override { Ptr element = new XmlElement; element->name.value = L"p"; XmlElementWriter writer(element); if (run->alignment) { switch (run->alignment.Value()) { case Alignment::Left: writer.Attribute(L"align", L"Left"); break; case Alignment::Center: writer.Attribute(L"align", L"Center"); break; case Alignment::Right: writer.Attribute(L"align", L"Right"); break; } } VisitContainer(element, run); } }; } using namespace document_operation_visitors; /*********************************************************************** DocumentModel ***********************************************************************/ Ptr DocumentModel::SaveToXml() { Ptr xml=new XmlDocument; Ptr doc=new XmlElement; doc->name.value=L"Doc"; xml->rootElement=doc; { Ptr content=new XmlElement; content->name.value=L"Content"; doc->subNodes.Add(content); FOREACH(Ptr, p, paragraphs) { SerializeRunVisitor visitor(this, content); p->Accept(&visitor); } } { Ptr stylesElement=new XmlElement; stylesElement->name.value=L"Styles"; doc->subNodes.Add(stylesElement); for(vint i=0;i0 && name[0] == L'#' && (name.Length() <= 9 || name.Right(9) != L"-Override")) continue; Ptr style=styles.Values().Get(i); Ptr sp=style->styles; Ptr styleElement=new XmlElement; styleElement->name.value=L"Style"; stylesElement->subNodes.Add(styleElement); XmlElementWriter(styleElement).Attribute(L"name", name); if(style->parentStyleName!=L"") { XmlElementWriter(styleElement).Attribute(L"parent", style->parentStyleName); } if(sp->face) XmlElementWriter(styleElement).Element(L"face").Text( sp->face.Value() ); if(sp->size) XmlElementWriter(styleElement).Element(L"size").Text( sp->size.Value().ToString() ); if(sp->color) XmlElementWriter(styleElement).Element(L"color").Text( sp->color.Value().ToString() ); if(sp->backgroundColor) XmlElementWriter(styleElement).Element(L"bkcolor").Text( sp->backgroundColor.Value().ToString() ); if(sp->bold) XmlElementWriter(styleElement).Element(L"b").Text( sp->bold.Value()?L"true":L"false" ); if(sp->italic) XmlElementWriter(styleElement).Element(L"i").Text( sp->italic.Value()?L"true":L"false" ); if(sp->underline) XmlElementWriter(styleElement).Element(L"u").Text( sp->underline.Value()?L"true":L"false" ); if(sp->strikeline) XmlElementWriter(styleElement).Element(L"s").Text( sp->strikeline.Value()?L"true":L"false" ); if(sp->antialias && sp->verticalAntialias) { bool h=sp->antialias; bool v=sp->verticalAntialias; if(!h) { XmlElementWriter(styleElement).Element(L"antialias").Text(L"no"); } else if(!v) { XmlElementWriter(styleElement).Element(L"antialias").Text(L"horizontal"); } else { XmlElementWriter(styleElement).Element(L"antialias").Text(L"vertical"); } } } } return xml; } } } /*********************************************************************** .\GRAPHICSCOMPOSITION\GUIGRAPHICSAXIS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace compositions { /*********************************************************************** GuiDefaultAxis ***********************************************************************/ GuiDefaultAxis::GuiDefaultAxis() { } GuiDefaultAxis::~GuiDefaultAxis() { } Size GuiDefaultAxis::RealSizeToVirtualSize(Size size) { return size; } Size GuiDefaultAxis::VirtualSizeToRealSize(Size size) { return size; } Point GuiDefaultAxis::RealPointToVirtualPoint(Size realFullSize, Point point) { return point; } Point GuiDefaultAxis::VirtualPointToRealPoint(Size realFullSize, Point point) { return point; } Rect GuiDefaultAxis::RealRectToVirtualRect(Size realFullSize, Rect rect) { return rect; } Rect GuiDefaultAxis::VirtualRectToRealRect(Size realFullSize, Rect rect) { return rect; } Margin GuiDefaultAxis::RealMarginToVirtualMargin(Margin margin) { return margin; } Margin GuiDefaultAxis::VirtualMarginToRealMargin(Margin margin) { return margin; } KeyDirection GuiDefaultAxis::RealKeyDirectionToVirtualKeyDirection(KeyDirection key) { return key; } /*********************************************************************** GuiAxis ***********************************************************************/ GuiAxis::GuiAxis(AxisDirection _axisDirection) :axisDirection(_axisDirection) { } GuiAxis::~GuiAxis() { } AxisDirection GuiAxis::GetDirection() { return axisDirection; } Size GuiAxis::RealSizeToVirtualSize(Size size) { switch(axisDirection) { case AxisDirection::LeftDown: case AxisDirection::RightDown: case AxisDirection::LeftUp: case AxisDirection::RightUp: return Size(size.x, size.y); case AxisDirection::DownLeft: case AxisDirection::DownRight: case AxisDirection::UpLeft: case AxisDirection::UpRight: return Size(size.y, size.x); } return size; } Size GuiAxis::VirtualSizeToRealSize(Size size) { return RealSizeToVirtualSize(size); } Point GuiAxis::RealPointToVirtualPoint(Size realFullSize, Point point) { Rect rect(point, Size(0, 0)); return RealRectToVirtualRect(realFullSize, rect).LeftTop(); } Point GuiAxis::VirtualPointToRealPoint(Size realFullSize, Point point) { Rect rect(point, Size(0, 0)); return VirtualRectToRealRect(realFullSize, rect).LeftTop(); } Rect GuiAxis::RealRectToVirtualRect(Size realFullSize, Rect rect) { vint x1=rect.x1; vint x2=realFullSize.x-rect.x2; vint y1=rect.y1; vint y2=realFullSize.y-rect.y2; vint w=rect.Width(); vint h=rect.Height(); switch(axisDirection) { case AxisDirection::LeftDown: return Rect(Point(x2, y1), Size(w, h)); case AxisDirection::RightDown: return Rect(Point(x1, y1), Size(w, h)); case AxisDirection::LeftUp: return Rect(Point(x2, y2), Size(w, h)); case AxisDirection::RightUp: return Rect(Point(x1, y2), Size(w, h)); case AxisDirection::DownLeft: return Rect(Point(y1, x2), Size(h, w)); case AxisDirection::DownRight: return Rect(Point(y1, x1), Size(h, w)); case AxisDirection::UpLeft: return Rect(Point(y2, x2), Size(h, w)); case AxisDirection::UpRight: return Rect(Point(y2, x1), Size(h, w)); } return rect; } Rect GuiAxis::VirtualRectToRealRect(Size realFullSize, Rect rect) { realFullSize=RealSizeToVirtualSize(realFullSize); vint x1=rect.x1; vint x2=realFullSize.x-rect.x2; vint y1=rect.y1; vint y2=realFullSize.y-rect.y2; vint w=rect.Width(); vint h=rect.Height(); switch(axisDirection) { case AxisDirection::LeftDown: return Rect(Point(x2, y1), Size(w, h)); case AxisDirection::RightDown: return Rect(Point(x1, y1), Size(w, h)); case AxisDirection::LeftUp: return Rect(Point(x2, y2), Size(w, h)); case AxisDirection::RightUp: return Rect(Point(x1, y2), Size(w, h)); case AxisDirection::DownLeft: return Rect(Point(y2, x1), Size(h, w)); case AxisDirection::DownRight: return Rect(Point(y1, x1), Size(h, w)); case AxisDirection::UpLeft: return Rect(Point(y2, x2), Size(h, w)); case AxisDirection::UpRight: return Rect(Point(y1, x2), Size(h, w)); } return rect; } Margin GuiAxis::RealMarginToVirtualMargin(Margin margin) { vint x1=margin.left; vint x2=margin.right; vint y1=margin.top; vint y2=margin.bottom; switch(axisDirection) { case AxisDirection::LeftDown: return Margin(x2, y1, x1, y2); case AxisDirection::RightDown: return Margin(x1, y1, x2, y2); case AxisDirection::LeftUp: return Margin(x2, y2, x1, y1); case AxisDirection::RightUp: return Margin(x1, y2, x2, y1); case AxisDirection::DownLeft: return Margin(y1, x2, y2, x1); case AxisDirection::DownRight: return Margin(y1, x1, y2, x2); case AxisDirection::UpLeft: return Margin(y2, x2, y1, x1); case AxisDirection::UpRight: return Margin(y2, x1, y1, x2); } return margin; } Margin GuiAxis::VirtualMarginToRealMargin(Margin margin) { vint x1=margin.left; vint x2=margin.right; vint y1=margin.top; vint y2=margin.bottom; switch(axisDirection) { case AxisDirection::LeftDown: return Margin(x2, y1, x1, y2); case AxisDirection::RightDown: return Margin(x1, y1, x2, y2); case AxisDirection::LeftUp: return Margin(x2, y2, x1, y1); case AxisDirection::RightUp: return Margin(x1, y2, x2, y1); case AxisDirection::DownLeft: return Margin(y2, x1, y1, x2); case AxisDirection::DownRight: return Margin(y1, x1, y2, x2); case AxisDirection::UpLeft: return Margin(y2, x2, y1, x1); case AxisDirection::UpRight: return Margin(y1, x2, y2, x1); default:; } return margin; } KeyDirection GuiAxis::RealKeyDirectionToVirtualKeyDirection(KeyDirection key) { bool pageKey=false; switch(key) { case KeyDirection::PageUp: pageKey=true; key=KeyDirection::Up; break; case KeyDirection::PageDown: pageKey=true; key=KeyDirection::Down; break; case KeyDirection::PageLeft: pageKey=true; key=KeyDirection::Left; break; case KeyDirection::PageRight: pageKey=true; key=KeyDirection::Right; break; default:; } switch(key) { case KeyDirection::Up: switch(axisDirection) { case AxisDirection::LeftDown: key=KeyDirection::Up; break; case AxisDirection::RightDown: key=KeyDirection::Up; break; case AxisDirection::LeftUp: key=KeyDirection::Down; break; case AxisDirection::RightUp: key=KeyDirection::Down; break; case AxisDirection::DownLeft: key=KeyDirection::Left; break; case AxisDirection::DownRight: key=KeyDirection::Left; break; case AxisDirection::UpLeft: key=KeyDirection::Right; break; case AxisDirection::UpRight: key=KeyDirection::Right; break; } break; case KeyDirection::Down: switch(axisDirection) { case AxisDirection::LeftDown: key=KeyDirection::Down; break; case AxisDirection::RightDown: key=KeyDirection::Down; break; case AxisDirection::LeftUp: key=KeyDirection::Up; break; case AxisDirection::RightUp: key=KeyDirection::Up; break; case AxisDirection::DownLeft: key=KeyDirection::Right; break; case AxisDirection::DownRight: key=KeyDirection::Right; break; case AxisDirection::UpLeft: key=KeyDirection::Left; break; case AxisDirection::UpRight: key=KeyDirection::Left; break; } break; case KeyDirection::Left: switch(axisDirection) { case AxisDirection::LeftDown: key=KeyDirection::Right; break; case AxisDirection::RightDown: key=KeyDirection::Left; break; case AxisDirection::LeftUp: key=KeyDirection::Right; break; case AxisDirection::RightUp: key=KeyDirection::Left; break; case AxisDirection::DownLeft: key=KeyDirection::Down; break; case AxisDirection::DownRight: key=KeyDirection::Up; break; case AxisDirection::UpLeft: key=KeyDirection::Down; break; case AxisDirection::UpRight: key=KeyDirection::Up; break; } break; case KeyDirection::Right: switch(axisDirection) { case AxisDirection::LeftDown: key=KeyDirection::Left; break; case AxisDirection::RightDown: key=KeyDirection::Right; break; case AxisDirection::LeftUp: key=KeyDirection::Left; break; case AxisDirection::RightUp: key=KeyDirection::Right; break; case AxisDirection::DownLeft: key=KeyDirection::Up; break; case AxisDirection::DownRight: key=KeyDirection::Down; break; case AxisDirection::UpLeft: key=KeyDirection::Up; break; case AxisDirection::UpRight: key=KeyDirection::Down; break; } break; case KeyDirection::Home: switch(axisDirection) { case AxisDirection::LeftDown: key=KeyDirection::Home; break; case AxisDirection::RightDown: key=KeyDirection::Home; break; case AxisDirection::LeftUp: key=KeyDirection::End; break; case AxisDirection::RightUp: key=KeyDirection::End; break; case AxisDirection::DownLeft: key=KeyDirection::Home; break; case AxisDirection::DownRight: key=KeyDirection::Home; break; case AxisDirection::UpLeft: key=KeyDirection::End; break; case AxisDirection::UpRight: key=KeyDirection::End; break; } break; case KeyDirection::End: switch(axisDirection) { case AxisDirection::LeftDown: key=KeyDirection::End; break; case AxisDirection::RightDown: key=KeyDirection::End; break; case AxisDirection::LeftUp: key=KeyDirection::Home; break; case AxisDirection::RightUp: key=KeyDirection::Home; break; case AxisDirection::DownLeft: key=KeyDirection::End; break; case AxisDirection::DownRight: key=KeyDirection::End; break; case AxisDirection::UpLeft: key=KeyDirection::Home; break; case AxisDirection::UpRight: key=KeyDirection::Home; break; } break; default:; } if(pageKey) { switch(key) { case KeyDirection::Up: key=KeyDirection::PageUp; break; case KeyDirection::Down: key=KeyDirection::PageDown; break; case KeyDirection::Left: key=KeyDirection::PageLeft; break; case KeyDirection::Right: key=KeyDirection::PageRight; break; default:; } } return key; } } } } /*********************************************************************** .\GRAPHICSCOMPOSITION\GUIGRAPHICSBASICCOMPOSITION.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace compositions { using namespace collections; using namespace controls; using namespace elements; /*********************************************************************** GuiWindowComposition ***********************************************************************/ GuiWindowComposition::GuiWindowComposition() { } GuiWindowComposition::~GuiWindowComposition() { } Rect GuiWindowComposition::GetBounds() { Rect bounds; if (relatedHostRecord) { if (auto window = relatedHostRecord->host->GetNativeWindow()) { bounds = Rect(Point(0, 0), window->GetClientSize()); } } UpdatePreviousBounds(bounds); return bounds; } void GuiWindowComposition::SetMargin(Margin value) { } /*********************************************************************** GuiBoundsComposition ***********************************************************************/ GuiBoundsComposition::GuiBoundsComposition() { } GuiBoundsComposition::~GuiBoundsComposition() { } bool GuiBoundsComposition::GetSizeAffectParent() { return sizeAffectParent; } void GuiBoundsComposition::SetSizeAffectParent(bool value) { sizeAffectParent = value; } bool GuiBoundsComposition::IsSizeAffectParent() { return sizeAffectParent; } Rect GuiBoundsComposition::GetPreferredBounds() { Rect result = GetBoundsInternal(compositionBounds); if (GetParent() && IsAlignedToParent()) { if (alignmentToParent.left >= 0) { vint offset = alignmentToParent.left - result.x1; result.x1 += offset; result.x2 += offset; } if (alignmentToParent.top >= 0) { vint offset = alignmentToParent.top - result.y1; result.y1 += offset; result.y2 += offset; } if (alignmentToParent.right >= 0) { result.x2 += alignmentToParent.right; } if (alignmentToParent.bottom >= 0) { result.y2 += alignmentToParent.bottom; } } return result; } Rect GuiBoundsComposition::GetBounds() { Rect result = GetPreferredBounds(); if (GetParent() && IsAlignedToParent()) { Size clientSize = GetParent()->GetClientArea().GetSize(); if (alignmentToParent.left >= 0 && alignmentToParent.right >= 0) { result.x1 = alignmentToParent.left; result.x2 = clientSize.x - alignmentToParent.right; } else if (alignmentToParent.left >= 0) { vint width = result.Width(); result.x1 = alignmentToParent.left; result.x2 = result.x1 + width; } else if (alignmentToParent.right >= 0) { vint width = result.Width(); result.x2 = clientSize.x - alignmentToParent.right; result.x1 = result.x2 - width; } if (alignmentToParent.top >= 0 && alignmentToParent.bottom >= 0) { result.y1 = alignmentToParent.top; result.y2 = clientSize.y - alignmentToParent.bottom; } else if (alignmentToParent.top >= 0) { vint height = result.Height(); result.y1 = alignmentToParent.top; result.y2 = result.y1 + height; } else if (alignmentToParent.bottom >= 0) { vint height = result.Height(); result.y2 = clientSize.y - alignmentToParent.bottom; result.y1 = result.y2 - height; } } UpdatePreviousBounds(result); return result; } void GuiBoundsComposition::SetBounds(Rect value) { compositionBounds = value; InvokeOnCompositionStateChanged(); } Margin GuiBoundsComposition::GetAlignmentToParent() { return alignmentToParent; } void GuiBoundsComposition::SetAlignmentToParent(Margin value) { alignmentToParent = value; InvokeOnCompositionStateChanged(); } bool GuiBoundsComposition::IsAlignedToParent() { return alignmentToParent != Margin(-1, -1, -1, -1); } } } } /*********************************************************************** .\GRAPHICSCOMPOSITION\GUIGRAPHICSFLOWCOMPOSITION.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace compositions { using namespace collections; /*********************************************************************** GuiFlowComposition ***********************************************************************/ void GuiFlowComposition::UpdateFlowItemBounds(bool forceUpdate) { if (forceUpdate || needUpdate) { needUpdate = false; InvokeOnCompositionStateChanged(); auto clientMargin = axis->RealMarginToVirtualMargin(extraMargin); if (clientMargin.left < 0) clientMargin.left = 0; if (clientMargin.top < 0) clientMargin.top = 0; if (clientMargin.right < 0) clientMargin.right = 0; if (clientMargin.bottom < 0) clientMargin.bottom = 0; auto realFullSize = previousBounds.GetSize(); auto clientSize = axis->RealSizeToVirtualSize(realFullSize); clientSize.x -= (clientMargin.left + clientMargin.right); clientSize.y -= (clientMargin.top + clientMargin.bottom); flowItemBounds.Resize(flowItems.Count()); for (vint i = 0; i < flowItems.Count(); i++) { flowItemBounds[i] = Rect(Point(0, 0), flowItems[i]->GetMinSize()); } vint currentIndex = 0; vint rowTop = 0; while (currentIndex < flowItems.Count()) { auto itemSize = axis->RealSizeToVirtualSize(flowItemBounds[currentIndex].GetSize()); vint rowWidth = itemSize.x; vint rowHeight = itemSize.y; vint rowItemCount = 1; for (vint i = currentIndex + 1; i < flowItems.Count(); i++) { itemSize = axis->RealSizeToVirtualSize(flowItemBounds[i].GetSize()); vint itemWidth = itemSize.x + columnPadding; if (rowWidth + itemWidth > clientSize.x) { break; } rowWidth += itemWidth; if (rowHeight < itemSize.y) { rowHeight = itemSize.y; } rowItemCount++; } vint baseLine = 0; Array itemBaseLines(rowItemCount); for (vint i = 0; i < rowItemCount; i++) { vint index = currentIndex + i; vint itemBaseLine = 0; itemSize = axis->RealSizeToVirtualSize(flowItemBounds[index].GetSize()); auto option = flowItems[index]->GetFlowOption(); switch (option.baseline) { case GuiFlowOption::FromTop: itemBaseLine = option.distance; break; case GuiFlowOption::FromBottom: itemBaseLine = itemSize.y - option.distance; break; case GuiFlowOption::Percentage: itemBaseLine = (vint)(itemSize.y*option.percentage); break; } itemBaseLines[i] = itemBaseLine; if (baseLine < itemBaseLine) { baseLine = itemBaseLine; } } vint rowUsedWidth = 0; for (vint i = 0; i < rowItemCount; i++) { vint index = currentIndex + i; itemSize = axis->RealSizeToVirtualSize(flowItemBounds[index].GetSize()); vint itemLeft = 0; vint itemTop = rowTop + baseLine - itemBaseLines[i]; switch (alignment) { case FlowAlignment::Left: itemLeft = rowUsedWidth + i * columnPadding; break; case FlowAlignment::Center: itemLeft = rowUsedWidth + i * columnPadding + (clientSize.x - rowWidth) / 2; break; case FlowAlignment::Extend: if (i == 0) { itemLeft = rowUsedWidth; } else { itemLeft = rowUsedWidth + (vint)((double)(clientSize.x - rowWidth) * i / (rowItemCount - 1)) + i * columnPadding; } break; } flowItemBounds[index] = axis->VirtualRectToRealRect( realFullSize, Rect( Point( itemLeft + clientMargin.left, itemTop + clientMargin.top ), itemSize ) ); rowUsedWidth += itemSize.x; } rowTop += rowHeight + rowPadding; currentIndex += rowItemCount; } minHeight = rowTop == 0 ? 0 : rowTop - rowPadding; } } void GuiFlowComposition::OnBoundsChanged(GuiGraphicsComposition* sender, GuiEventArgs& arguments) { UpdateFlowItemBounds(true); } void GuiFlowComposition::OnChildInserted(GuiGraphicsComposition* child) { GuiBoundsComposition::OnChildInserted(child); auto item = dynamic_cast(child); if (item && !flowItems.Contains(item)) { flowItems.Add(item); needUpdate = true; } } void GuiFlowComposition::OnChildRemoved(GuiGraphicsComposition* child) { GuiBoundsComposition::OnChildRemoved(child); auto item = dynamic_cast(child); if (item) { flowItems.Remove(item); needUpdate = true; } } GuiFlowComposition::GuiFlowComposition() :axis(new GuiDefaultAxis) { BoundsChanged.AttachMethod(this, &GuiFlowComposition::OnBoundsChanged); } GuiFlowComposition::~GuiFlowComposition() { } const GuiFlowComposition::ItemCompositionList& GuiFlowComposition::GetFlowItems() { return flowItems; } bool GuiFlowComposition::InsertFlowItem(vint index, GuiFlowItemComposition* item) { index = flowItems.Insert(index, item); if (!AddChild(item)) { flowItems.RemoveAt(index); return false; } else { needUpdate = true; return true; } } Margin GuiFlowComposition::GetExtraMargin() { return extraMargin; } void GuiFlowComposition::SetExtraMargin(Margin value) { extraMargin = value; needUpdate = true; InvokeOnCompositionStateChanged(); } vint GuiFlowComposition::GetRowPadding() { return rowPadding; } void GuiFlowComposition::SetRowPadding(vint value) { rowPadding = value; needUpdate = true; InvokeOnCompositionStateChanged(); } vint GuiFlowComposition::GetColumnPadding() { return columnPadding; } void GuiFlowComposition::SetColumnPadding(vint value) { columnPadding = value; needUpdate = true; InvokeOnCompositionStateChanged(); } Ptr GuiFlowComposition::GetAxis() { return axis; } void GuiFlowComposition::SetAxis(Ptr value) { if (value) { axis = value; needUpdate = true; InvokeOnCompositionStateChanged(); } } FlowAlignment GuiFlowComposition::GetAlignment() { return alignment; } void GuiFlowComposition::SetAlignment(FlowAlignment value) { alignment = value; needUpdate = true; InvokeOnCompositionStateChanged(); } void GuiFlowComposition::ForceCalculateSizeImmediately() { GuiBoundsComposition::ForceCalculateSizeImmediately(); UpdateFlowItemBounds(true); } Size GuiFlowComposition::GetMinPreferredClientSize() { Size minSize = GuiBoundsComposition::GetMinPreferredClientSize(); if (GetMinSizeLimitation() == GuiGraphicsComposition::LimitToElementAndChildren) { auto clientSize = axis->VirtualSizeToRealSize(Size(0, minHeight)); FOREACH(GuiFlowItemComposition*, item, flowItems) { auto itemSize = item->GetPreferredBounds().GetSize(); if (clientSize.x < itemSize.x) clientSize.x = itemSize.x; if (clientSize.y < itemSize.y) clientSize.y = itemSize.y; } if (minSize.x < clientSize.x) minSize.x = clientSize.x; if (minSize.y < clientSize.y) minSize.y = clientSize.y; } vint x = 0; vint y = 0; if (extraMargin.left > 0) x += extraMargin.left; if (extraMargin.right > 0) x += extraMargin.right; if (extraMargin.top > 0) y += extraMargin.top; if (extraMargin.bottom > 0) y += extraMargin.bottom; return minSize + Size(x, y); } Rect GuiFlowComposition::GetBounds() { if (!needUpdate) { for (vint i = 0; i < flowItems.Count(); i++) { if (flowItemBounds[i].GetSize() != flowItems[i]->GetMinSize()) { needUpdate = true; break; } } } if (needUpdate) { UpdateFlowItemBounds(true); } bounds = GuiBoundsComposition::GetBounds(); return bounds; } /*********************************************************************** GuiFlowItemComposition ***********************************************************************/ void GuiFlowItemComposition::OnParentChanged(GuiGraphicsComposition* oldParent, GuiGraphicsComposition* newParent) { GuiGraphicsSite::OnParentChanged(oldParent, newParent); flowParent = newParent == 0 ? 0 : dynamic_cast(newParent); } Size GuiFlowItemComposition::GetMinSize() { return GetBoundsInternal(bounds).GetSize(); } GuiFlowItemComposition::GuiFlowItemComposition() { SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); } GuiFlowItemComposition::~GuiFlowItemComposition() { } bool GuiFlowItemComposition::IsSizeAffectParent() { return false; } Rect GuiFlowItemComposition::GetBounds() { Rect result = bounds; if(flowParent) { flowParent->UpdateFlowItemBounds(false); vint index = flowParent->flowItems.IndexOf(this); if (index != -1) { result = flowParent->flowItemBounds[index]; } result = Rect( result.Left() - extraMargin.left, result.Top() - extraMargin.top, result.Right() + extraMargin.right, result.Bottom() + extraMargin.bottom ); } UpdatePreviousBounds(result); return result; } void GuiFlowItemComposition::SetBounds(Rect value) { bounds = value; InvokeOnCompositionStateChanged(); } Margin GuiFlowItemComposition::GetExtraMargin() { return extraMargin; } void GuiFlowItemComposition::SetExtraMargin(Margin value) { extraMargin = value; InvokeOnCompositionStateChanged(); } GuiFlowOption GuiFlowItemComposition::GetFlowOption() { return option; } void GuiFlowItemComposition::SetFlowOption(GuiFlowOption value) { option = value; if (flowParent) { flowParent->needUpdate = true; InvokeOnCompositionStateChanged(); } } } } } /*********************************************************************** .\CONTROLS\GUILABELCONTROLS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace elements; using namespace compositions; using namespace collections; using namespace reflection::description; /*********************************************************************** GuiLabel ***********************************************************************/ void GuiLabel::BeforeControlTemplateUninstalled_() { auto ct = GetControlTemplateObject(false); if (!ct) return; textColorConsisted = (textColor == ct->GetDefaultTextColor()); } void GuiLabel::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); if (initialize || textColorConsisted) { SetTextColor(ct->GetDefaultTextColor()); } else { ct->SetTextColor(textColor); } } GuiLabel::GuiLabel(theme::ThemeName themeName) :GuiControl(themeName) { } GuiLabel::~GuiLabel() { } Color GuiLabel::GetTextColor() { return textColor; } void GuiLabel::SetTextColor(Color value) { if (textColor != value) { textColor = value; GetControlTemplateObject(true)->SetTextColor(textColor); } } } } } /*********************************************************************** .\CONTROLS\GUIWINDOWCONTROLS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace elements; using namespace compositions; using namespace collections; using namespace reflection::description; /*********************************************************************** GuiControlHost ***********************************************************************/ void GuiControlHost::OnNativeWindowChanged() { } void GuiControlHost::OnVisualStatusChanged() { } controls::GuiControlHost* GuiControlHost::GetControlHostForInstance() { return this; } GuiControl* GuiControlHost::GetTooltipOwner(Point location) { GuiGraphicsComposition* composition=this->GetBoundsComposition()->FindComposition(location, false); if(composition) { GuiControl* control=composition->GetRelatedControl(); while(control) { if(control->GetTooltipControl()) { return control; } control=control->GetParent(); } } return 0; } void GuiControlHost::MoveIntoTooltipControl(GuiControl* tooltipControl, Point location) { if(tooltipLocation!=location) { tooltipLocation=location; { GuiControl* currentOwner=GetApplication()->GetTooltipOwner(); if(currentOwner && currentOwner!=tooltipControl) { if(tooltipCloseDelay) { tooltipCloseDelay->Cancel(); tooltipCloseDelay=0; } GetApplication()->DelayExecuteInMainThread([=]() { currentOwner->CloseTooltip(); }, TooltipDelayCloseTime); } } if(!tooltipControl) { if(tooltipOpenDelay) { tooltipOpenDelay->Cancel(); tooltipOpenDelay=0; } } else if(tooltipOpenDelay) { tooltipOpenDelay->Delay(TooltipDelayOpenTime); } else if(GetApplication()->GetTooltipOwner()!=tooltipControl) { tooltipOpenDelay=GetApplication()->DelayExecuteInMainThread([this]() { GuiControl* owner=GetTooltipOwner(tooltipLocation); if(owner) { Point offset=owner->GetBoundsComposition()->GetGlobalBounds().LeftTop(); Point p(tooltipLocation.x-offset.x, tooltipLocation.y-offset.y+24); owner->DisplayTooltip(p); tooltipOpenDelay=0; tooltipCloseDelay=GetApplication()->DelayExecuteInMainThread([this, owner]() { owner->CloseTooltip(); }, TooltipDelayLifeTime); } }, TooltipDelayOpenTime); } } } void GuiControlHost::MouseMoving(const NativeWindowMouseInfo& info) { if(!info.left && !info.middle && !info.right) { GuiControl* tooltipControl=GetTooltipOwner(tooltipLocation); MoveIntoTooltipControl(tooltipControl, Point(info.x, info.y)); } } void GuiControlHost::MouseLeaved() { MoveIntoTooltipControl(0, Point(-1, -1)); } void GuiControlHost::Moved() { OnVisualStatusChanged(); } void GuiControlHost::Enabled() { GuiControl::SetEnabled(true); OnVisualStatusChanged(); } void GuiControlHost::Disabled() { GuiControl::SetEnabled(false); OnVisualStatusChanged(); } void GuiControlHost::GotFocus() { WindowGotFocus.Execute(GetNotifyEventArguments()); OnVisualStatusChanged(); } void GuiControlHost::LostFocus() { WindowLostFocus.Execute(GetNotifyEventArguments()); OnVisualStatusChanged(); } void GuiControlHost::Activated() { WindowActivated.Execute(GetNotifyEventArguments()); OnVisualStatusChanged(); } void GuiControlHost::Deactivated() { WindowDeactivated.Execute(GetNotifyEventArguments()); OnVisualStatusChanged(); } void GuiControlHost::Opened() { WindowOpened.Execute(GetNotifyEventArguments()); } void GuiControlHost::Closing(bool& cancel) { GuiRequestEventArgs arguments(boundsComposition); arguments.cancel=cancel; WindowClosing.Execute(arguments); if(!arguments.handled) { cancel=arguments.cancel; } } void GuiControlHost::Closed() { WindowClosed.Execute(GetNotifyEventArguments()); } void GuiControlHost::Destroying() { WindowDestroying.Execute(GetNotifyEventArguments()); SetNativeWindow(0); } void GuiControlHost::UpdateClientSizeAfterRendering(Size clientSize) { SetClientSize(clientSize); } GuiControlHost::GuiControlHost(theme::ThemeName themeName) :GuiControl(themeName) { boundsComposition->SetAlignmentToParent(Margin(0, 0, 0, 0)); WindowGotFocus.SetAssociatedComposition(boundsComposition); WindowLostFocus.SetAssociatedComposition(boundsComposition); WindowActivated.SetAssociatedComposition(boundsComposition); WindowDeactivated.SetAssociatedComposition(boundsComposition); WindowOpened.SetAssociatedComposition(boundsComposition); WindowClosing.SetAssociatedComposition(boundsComposition); WindowClosed.SetAssociatedComposition(boundsComposition); WindowDestroying.SetAssociatedComposition(boundsComposition); host=new GuiGraphicsHost(this, boundsComposition); sharedPtrDestructorProc = 0; } GuiControlHost::~GuiControlHost() { FinalizeInstanceRecursively(this); OnBeforeReleaseGraphicsHost(); delete host; } compositions::GuiGraphicsHost* GuiControlHost::GetGraphicsHost() { return host; } compositions::GuiGraphicsComposition* GuiControlHost::GetMainComposition() { return host->GetMainComposition(); } INativeWindow* GuiControlHost::GetNativeWindow() { return host->GetNativeWindow(); } void GuiControlHost::SetNativeWindow(INativeWindow* window) { if(host->GetNativeWindow()) { host->GetNativeWindow()->UninstallListener(this); } host->SetNativeWindow(window); if(host->GetNativeWindow()) { host->GetNativeWindow()->InstallListener(this); } OnNativeWindowChanged(); } void GuiControlHost::ForceCalculateSizeImmediately() { boundsComposition->ForceCalculateSizeImmediately(); SetBounds(GetBounds()); } bool GuiControlHost::GetEnabled() { if(host->GetNativeWindow()) { return host->GetNativeWindow()->IsEnabled(); } else { return false; } } void GuiControlHost::SetEnabled(bool value) { if(host->GetNativeWindow()) { if(value) { host->GetNativeWindow()->Enable(); } else { host->GetNativeWindow()->Disable(); } } } bool GuiControlHost::GetFocused() { if(host->GetNativeWindow()) { return host->GetNativeWindow()->IsFocused(); } else { return false; } } void GuiControlHost::SetFocused() { if(host->GetNativeWindow()) { host->GetNativeWindow()->SetFocus(); } } bool GuiControlHost::GetActivated() { if(host->GetNativeWindow()) { return host->GetNativeWindow()->IsActivated(); } else { return false; } } void GuiControlHost::SetActivated() { if(host->GetNativeWindow()) { host->GetNativeWindow()->SetActivate(); } } bool GuiControlHost::GetShowInTaskBar() { if(host->GetNativeWindow()) { return host->GetNativeWindow()->IsAppearedInTaskBar(); } else { return false; } } void GuiControlHost::SetShowInTaskBar(bool value) { if(host->GetNativeWindow()) { if(value) { host->GetNativeWindow()->ShowInTaskBar(); } else { host->GetNativeWindow()->HideInTaskBar(); } } } bool GuiControlHost::GetEnabledActivate() { if(host->GetNativeWindow()) { return host->GetNativeWindow()->IsEnabledActivate(); } else { return false; } } void GuiControlHost::SetEnabledActivate(bool value) { if(host->GetNativeWindow()) { if(value) { host->GetNativeWindow()->EnableActivate(); } else { host->GetNativeWindow()->DisableActivate(); } } } bool GuiControlHost::GetTopMost() { if(host->GetNativeWindow()) { return host->GetNativeWindow()->GetTopMost(); } else { return false; } } void GuiControlHost::SetTopMost(bool topmost) { if(host->GetNativeWindow()) { host->GetNativeWindow()->SetTopMost(topmost); } } compositions::IGuiShortcutKeyManager* GuiControlHost::GetShortcutKeyManager() { return host->GetShortcutKeyManager(); } void GuiControlHost::SetShortcutKeyManager(compositions::IGuiShortcutKeyManager* value) { host->SetShortcutKeyManager(value); } compositions::GuiGraphicsTimerManager* GuiControlHost::GetTimerManager() { return host->GetTimerManager(); } Size GuiControlHost::GetClientSize() { if(host->GetNativeWindow()) { return host->GetNativeWindow()->GetClientSize(); } else { return Size(0, 0); } } void GuiControlHost::SetClientSize(Size value) { if(host->GetNativeWindow()) { host->GetNativeWindow()->SetClientSize(value); } } Rect GuiControlHost::GetBounds() { if(host->GetNativeWindow()) { return host->GetNativeWindow()->GetBounds(); } else { return Rect(); } } void GuiControlHost::SetBounds(Rect value) { if(host->GetNativeWindow()) { host->GetNativeWindow()->SetBounds(value); } } GuiControlHost* GuiControlHost::GetRelatedControlHost() { return this; } const WString& GuiControlHost::GetText() { WString result; if(host->GetNativeWindow()) { result=host->GetNativeWindow()->GetTitle(); } if(result!=GuiControl::GetText()) { GuiControl::SetText(result); } return GuiControl::GetText(); } void GuiControlHost::SetText(const WString& value) { if(host->GetNativeWindow()) { host->GetNativeWindow()->SetTitle(value); GuiControl::SetText(value); } } INativeScreen* GuiControlHost::GetRelatedScreen() { if(host->GetNativeWindow()) { return GetCurrentController()->ScreenService()->GetScreen(host->GetNativeWindow()); } else { return 0; } } void GuiControlHost::Show() { if(host->GetNativeWindow()) { host->GetNativeWindow()->Show(); } } void GuiControlHost::ShowDeactivated() { if(host->GetNativeWindow()) { host->GetNativeWindow()->ShowDeactivated(); } } void GuiControlHost::ShowRestored() { if(host->GetNativeWindow()) { host->GetNativeWindow()->ShowRestored(); } } void GuiControlHost::ShowMaximized() { if(host->GetNativeWindow()) { host->GetNativeWindow()->ShowMaximized(); } } void GuiControlHost::ShowMinimized() { if(host->GetNativeWindow()) { host->GetNativeWindow()->ShowMinimized(); } } void GuiControlHost::Hide() { if(host->GetNativeWindow()) { host->GetNativeWindow()->Hide(); } } void GuiControlHost::Close() { INativeWindow* window=host->GetNativeWindow(); if(window) { if(GetCurrentController()->WindowService()->GetMainWindow()!=window) { window->Hide(); } else { SetNativeWindow(0); GetCurrentController()->WindowService()->DestroyNativeWindow(window); } } } bool GuiControlHost::GetOpening() { INativeWindow* window=host->GetNativeWindow(); if(window) { return window->IsVisible(); } return false; } /*********************************************************************** GuiWindow ***********************************************************************/ void GuiWindow::BeforeControlTemplateUninstalled_() { } void GuiWindow::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); #define FIX_WINDOW_PROPERTY(VARIABLE, NAME) \ switch (ct->Get ## NAME ## Option()) \ { \ case templates::BoolOption::AlwaysTrue: \ VARIABLE = true; \ break; \ case templates::BoolOption::AlwaysFalse: \ VARIABLE = false; \ break; \ default:; \ } \ FIX_WINDOW_PROPERTY(hasMaximizedBox, MaximizedBox) FIX_WINDOW_PROPERTY(hasMinimizedBox, MinimizedBox) FIX_WINDOW_PROPERTY(hasBorder, Border) FIX_WINDOW_PROPERTY(hasSizeBox, SizeBox) FIX_WINDOW_PROPERTY(isIconVisible, IconVisible) FIX_WINDOW_PROPERTY(hasTitleBar, TitleBar) #undef FIX_WINDOW_PROPERTY ct->SetMaximizedBox(hasMaximizedBox); ct->SetMinimizedBox(hasMinimizedBox); ct->SetBorder(hasBorder); ct->SetSizeBox(hasSizeBox); ct->SetIconVisible(isIconVisible); ct->SetTitleBar(hasTitleBar); ct->SetMaximized(GetNativeWindow()->GetSizeState() != INativeWindow::Maximized); SyncNativeWindowProperties(); } void GuiWindow::SyncNativeWindowProperties() { if (auto window = GetNativeWindow()) { if (GetControlTemplateObject(true)->GetCustomFrameEnabled()) { window->EnableCustomFrameMode(); window->SetBorder(false); } else { window->DisableCustomFrameMode(); window->SetBorder(hasBorder); } window->SetMaximizedBox(hasMaximizedBox); window->SetMinimizedBox(hasMinimizedBox); window->SetSizeBox(hasSizeBox); window->SetIconVisible(isIconVisible); window->SetTitleBar(hasTitleBar); } } void GuiWindow::Moved() { GuiControlHost::Moved(); GetControlTemplateObject(true)->SetMaximized(GetNativeWindow()->GetSizeState() != INativeWindow::Maximized); } void GuiWindow::OnNativeWindowChanged() { SyncNativeWindowProperties(); GuiControlHost::OnNativeWindowChanged(); } void GuiWindow::OnVisualStatusChanged() { GuiControlHost::OnVisualStatusChanged(); } void GuiWindow::MouseClickedOnOtherWindow(GuiWindow* window) { } compositions::GuiGraphicsComposition* GuiWindow::GetAltComposition() { return boundsComposition; } compositions::IGuiAltActionHost* GuiWindow::GetPreviousAltHost() { return previousAltHost; } void GuiWindow::OnActivatedAltHost(IGuiAltActionHost* previousHost) { previousAltHost = previousHost; } void GuiWindow::OnDeactivatedAltHost() { previousAltHost = 0; } void GuiWindow::CollectAltActions(collections::Group& actions) { IGuiAltActionHost::CollectAltActionsFromControl(this, actions); } GuiWindow::GuiWindow(theme::ThemeName themeName) :GuiControlHost(themeName) ,previousAltHost(0) { INativeWindow* window=GetCurrentController()->WindowService()->CreateNativeWindow(); SetNativeWindow(window); GetApplication()->RegisterWindow(this); ClipboardUpdated.SetAssociatedComposition(boundsComposition); } GuiWindow::~GuiWindow() { FinalizeAggregation(); GetApplication()->UnregisterWindow(this); INativeWindow* window=host->GetNativeWindow(); if(window) { SetNativeWindow(0); GetCurrentController()->WindowService()->DestroyNativeWindow(window); } } IDescriptable* GuiWindow::QueryService(const WString& identifier) { if (identifier == IGuiAltActionHost::Identifier) { return (IGuiAltActionHost*)this; } else { return GuiControlHost::QueryService(identifier); } } void GuiWindow::MoveToScreenCenter() { MoveToScreenCenter(GetRelatedScreen()); } void GuiWindow::MoveToScreenCenter(INativeScreen* screen) { if (screen) { Rect screenBounds = screen->GetClientBounds(); Rect windowBounds = GetBounds(); SetBounds( Rect( Point( screenBounds.Left() + (screenBounds.Width() - windowBounds.Width()) / 2, screenBounds.Top() + (screenBounds.Height() - windowBounds.Height()) / 2 ), windowBounds.GetSize() ) ); } } #define IMPL_WINDOW_PROPERTY(VARIABLE, NAME, CONDITION_BREAK) \ bool GuiWindow::Get ## NAME() \ { \ return VARIABLE; \ } \ void GuiWindow::Set ## NAME(bool visible) \ { \ auto ct = GetControlTemplateObject(true); \ if (ct->Get ## NAME ## Option() == templates::BoolOption::Customizable) \ { \ VARIABLE = visible; \ ct->Set ## NAME(visible); \ if (auto window = GetNativeWindow()) \ { \ CONDITION_BREAK \ window->Set ## NAME(visible); \ } \ } \ } \ #define IMPL_WINDOW_PROPERTY_EMPTY_CONDITION #define IMPL_WINDOW_PROPERTY_BORDER_CONDITION if (ct->GetCustomFrameEnabled()) return; IMPL_WINDOW_PROPERTY(hasMaximizedBox, MaximizedBox, IMPL_WINDOW_PROPERTY_EMPTY_CONDITION) IMPL_WINDOW_PROPERTY(hasMinimizedBox, MinimizedBox, IMPL_WINDOW_PROPERTY_EMPTY_CONDITION) IMPL_WINDOW_PROPERTY(hasBorder, Border, IMPL_WINDOW_PROPERTY_BORDER_CONDITION) IMPL_WINDOW_PROPERTY(hasSizeBox, SizeBox, IMPL_WINDOW_PROPERTY_EMPTY_CONDITION) IMPL_WINDOW_PROPERTY(isIconVisible, IconVisible, IMPL_WINDOW_PROPERTY_EMPTY_CONDITION) IMPL_WINDOW_PROPERTY(hasTitleBar, TitleBar, IMPL_WINDOW_PROPERTY_EMPTY_CONDITION) #undef IMPL_WINDOW_PROPERTY_BORDER_CONDITION #undef IMPL_WINDOW_PROPERTY_EMPTY_CONDITION #undef IMPL_WINDOW_PROPERTY void GuiWindow::ShowModal(GuiWindow* owner, const Func& callback) { owner->SetEnabled(false); GetNativeWindow()->SetParent(owner->GetNativeWindow()); auto container = MakePtr(); container->handler = WindowClosed.AttachLambda([=](GuiGraphicsComposition* sender, GuiEventArgs& arguments) { GetApplication()->InvokeInMainThread(this, [=]() { WindowClosed.Detach(container->handler); container->handler = nullptr; GetNativeWindow()->SetParent(0); callback(); owner->SetEnabled(true); owner->SetActivated(); }); }); Show(); } void GuiWindow::ShowModalAndDelete(GuiWindow* owner, const Func& callback) { ShowModal(owner, [=]() { callback(); delete this; }); } Ptr GuiWindow::ShowModalAsync(GuiWindow* owner) { auto future = IFuture::Create(); ShowModal(owner, [promise = future->GetPromise()]() { promise->SendResult({}); }); return future; } /*********************************************************************** GuiPopup ***********************************************************************/ void GuiPopup::UpdateClientSizeAfterRendering(Size clientSize) { if (popupType == -1) { GuiWindow::UpdateClientSizeAfterRendering(clientSize); } else { auto window = GetNativeWindow(); auto position = CalculatePopupPosition(clientSize, popupType, popupInfo); window->SetBounds(Rect(position, clientSize)); } } void GuiPopup::MouseClickedOnOtherWindow(GuiWindow* window) { Hide(); } void GuiPopup::PopupOpened(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { GetApplication()->RegisterPopupOpened(this); } void GuiPopup::PopupClosed(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { popupType = -1; GetApplication()->RegisterPopupClosed(this); if(auto window = GetNativeWindow()) { window->SetParent(nullptr); } } Point GuiPopup::CalculatePopupPosition(Size size, Point location, INativeScreen* screen) { Rect screenBounds = screen->GetClientBounds(); if (location.x < screenBounds.x1) { location.x = screenBounds.x1; } else if (location.x + size.x > screenBounds.x2) { location.x = screenBounds.x2 - size.x; } if (location.y < screenBounds.y1) { location.y = screenBounds.y1; } else if (location.y + size.y > screenBounds.y2) { location.y = screenBounds.y2 - size.y; } return location; } bool GuiPopup::IsClippedByScreen(Size size, Point location, INativeScreen* screen) { Rect screenBounds = screen->GetClientBounds(); Rect windowBounds(location, size); return !screenBounds.Contains(windowBounds.LeftTop()) || !screenBounds.Contains(windowBounds.RightBottom()); } Point GuiPopup::CalculatePopupPosition(Size size, GuiControl* control, INativeWindow* controlWindow, Rect bounds, bool preferredTopBottomSide) { Point controlClientOffset = control->GetBoundsComposition()->GetGlobalBounds().LeftTop(); Point controlWindowOffset = controlWindow->GetClientBoundsInScreen().LeftTop(); bounds.x1 += controlClientOffset.x + controlWindowOffset.x; bounds.x2 += controlClientOffset.x + controlWindowOffset.x; bounds.y1 += controlClientOffset.y + controlWindowOffset.y; bounds.y2 += controlClientOffset.y + controlWindowOffset.y; Point locations[4]; if (preferredTopBottomSide) { locations[0] = Point(bounds.x1, bounds.y2); locations[1] = Point(bounds.x2 - size.x, bounds.y2); locations[2] = Point(bounds.x1, bounds.y1 - size.y); locations[3] = Point(bounds.x2 - size.x, bounds.y1 - size.y); } else { locations[0] = Point(bounds.x2, bounds.y1); locations[1] = Point(bounds.x2, bounds.y2 - size.y); locations[2] = Point(bounds.x1 - size.x, bounds.y1); locations[3] = Point(bounds.x1 - size.x, bounds.y2 - size.y); } auto screen = GetCurrentController()->ScreenService()->GetScreen(controlWindow); for (vint i = 0; i < 4; i++) { if (!IsClippedByScreen(size, locations[i], screen)) { return CalculatePopupPosition(size, locations[i], screen); } } return CalculatePopupPosition(size, locations[0], screen); } Point GuiPopup::CalculatePopupPosition(Size size, GuiControl* control, INativeWindow* controlWindow, Point location) { Point locations[4]; Rect controlBounds = control->GetBoundsComposition()->GetGlobalBounds(); Point controlClientOffset = controlWindow->GetClientBoundsInScreen().LeftTop(); vint x = controlBounds.x1 + controlClientOffset.x + location.x; vint y = controlBounds.y1 + controlClientOffset.y + location.y; return CalculatePopupPosition(size, Point(x, y), GetCurrentController()->ScreenService()->GetScreen(controlWindow)); } Point GuiPopup::CalculatePopupPosition(Size size, GuiControl* control, INativeWindow* controlWindow, bool preferredTopBottomSide) { Rect bounds(Point(0, 0), control->GetBoundsComposition()->GetBounds().GetSize()); return CalculatePopupPosition(size, control, controlWindow, bounds, preferredTopBottomSide); } Point GuiPopup::CalculatePopupPosition(Size size, vint popupType, const PopupInfo& popupInfo) { switch (popupType) { case 1: return CalculatePopupPosition(size, popupInfo._1.location, popupInfo._1.screen); case 2: return CalculatePopupPosition(size, popupInfo._2.control, popupInfo._2.controlWindow, popupInfo._2.bounds, popupInfo._2.preferredTopBottomSide); case 3: return CalculatePopupPosition(size, popupInfo._3.control, popupInfo._3.controlWindow, popupInfo._3.location); case 4: return CalculatePopupPosition(size, popupInfo._4.control, popupInfo._4.controlWindow, popupInfo._4.preferredTopBottomSide); default: CHECK_FAIL(L"vl::presentation::controls::GuiPopup::CalculatePopupPosition(Size, const PopupInfo&)#Internal error."); } } void GuiPopup::ShowPopupInternal() { auto window = GetNativeWindow(); UpdateClientSizeAfterRendering(window->GetBounds().GetSize()); switch (popupType) { case 2: window->SetParent(popupInfo._2.controlWindow); break; case 3: window->SetParent(popupInfo._3.controlWindow); break; case 4: window->SetParent(popupInfo._4.controlWindow); break; } ShowDeactivated(); } GuiPopup::GuiPopup(theme::ThemeName themeName) :GuiWindow(themeName) { SetMinimizedBox(false); SetMaximizedBox(false); SetSizeBox(false); SetTitleBar(false); SetShowInTaskBar(false); WindowOpened.AttachMethod(this, &GuiPopup::PopupOpened); WindowClosed.AttachMethod(this, &GuiPopup::PopupClosed); } GuiPopup::~GuiPopup() { GetApplication()->RegisterPopupClosed(this); } void GuiPopup::ShowPopup(Point location, INativeScreen* screen) { if (auto window = GetNativeWindow()) { if (!screen) { SetBounds(Rect(location, GetBounds().GetSize())); screen = GetCurrentController()->ScreenService()->GetScreen(window); } popupType = 1; popupInfo._1.location = location; popupInfo._1.screen = screen; ShowPopupInternal(); } } void GuiPopup::ShowPopup(GuiControl* control, Rect bounds, bool preferredTopBottomSide) { if (auto window = GetNativeWindow()) { if (auto controlHost = control->GetBoundsComposition()->GetRelatedControlHost()) { if (auto controlWindow = controlHost->GetNativeWindow()) { popupType = 2; popupInfo._2.control = control; popupInfo._2.controlWindow = controlWindow; popupInfo._2.bounds = bounds; popupInfo._2.preferredTopBottomSide = preferredTopBottomSide; ShowPopupInternal(); } } } } void GuiPopup::ShowPopup(GuiControl* control, Point location) { if (auto window = GetNativeWindow()) { if (auto controlHost = control->GetBoundsComposition()->GetRelatedControlHost()) { if (auto controlWindow = controlHost->GetNativeWindow()) { popupType = 3; popupInfo._3.control = control; popupInfo._3.controlWindow = controlWindow; popupInfo._3.location = location; ShowPopupInternal(); } } } } void GuiPopup::ShowPopup(GuiControl* control, bool preferredTopBottomSide) { if (auto window = GetNativeWindow()) { if (auto controlHost = control->GetBoundsComposition()->GetRelatedControlHost()) { if (auto controlWindow = controlHost->GetNativeWindow()) { popupType = 4; popupInfo._4.control = control; popupInfo._4.controlWindow = controlWindow; popupInfo._4.preferredTopBottomSide = preferredTopBottomSide; ShowPopupInternal(); } } } } /*********************************************************************** GuiPopup ***********************************************************************/ void GuiTooltip::GlobalTimer() { SetClientSize(GetClientSize()); } void GuiTooltip::TooltipOpened(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { } void GuiTooltip::TooltipClosed(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { SetTemporaryContentControl(0); } GuiTooltip::GuiTooltip(theme::ThemeName themeName) :GuiPopup(themeName) ,temporaryContentControl(0) { containerComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); containerComposition->SetPreferredMinSize(Size(20, 10)); GetCurrentController()->CallbackService()->InstallListener(this); WindowOpened.AttachMethod(this, &GuiTooltip::TooltipOpened); WindowClosed.AttachMethod(this, &GuiTooltip::TooltipClosed); } GuiTooltip::~GuiTooltip() { GetCurrentController()->CallbackService()->UninstallListener(this); } vint GuiTooltip::GetPreferredContentWidth() { return containerComposition->GetPreferredMinSize().x; } void GuiTooltip::SetPreferredContentWidth(vint value) { containerComposition->SetPreferredMinSize(Size(value, 10)); } GuiControl* GuiTooltip::GetTemporaryContentControl() { return temporaryContentControl; } void GuiTooltip::SetTemporaryContentControl(GuiControl* control) { if(temporaryContentControl && HasChild(temporaryContentControl)) { containerComposition->RemoveChild(temporaryContentControl->GetBoundsComposition()); temporaryContentControl=0; } temporaryContentControl=control; if(control) { control->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); AddChild(control); } } } } } /*********************************************************************** .\GRAPHICSCOMPOSITION\GUIGRAPHICSCOMPOSITION.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace compositions { using namespace reflection::description; using namespace collections; using namespace controls; using namespace elements; /*********************************************************************** GuiSharedSizeItemComposition ***********************************************************************/ void GuiSharedSizeItemComposition::Update() { if (parentRoot) { parentRoot->ForceCalculateSizeImmediately(); } InvokeOnCompositionStateChanged(); } void GuiSharedSizeItemComposition::OnParentLineChanged() { GuiBoundsComposition::OnParentLineChanged(); if (parentRoot) { parentRoot->childItems.Remove(this); parentRoot = 0; } auto current = GetParent(); while (current) { if (auto item = dynamic_cast(current)) { break; } else if (auto root = dynamic_cast(current)) { parentRoot = root; break; } current = current->GetParent(); } if (parentRoot) { parentRoot->childItems.Add(this); } } GuiSharedSizeItemComposition::GuiSharedSizeItemComposition() :parentRoot(0) , sharedWidth(false) , sharedHeight(false) { SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); } GuiSharedSizeItemComposition::~GuiSharedSizeItemComposition() { } const WString& GuiSharedSizeItemComposition::GetGroup() { return group; } void GuiSharedSizeItemComposition::SetGroup(const WString& value) { if (group != value) { group = value; Update(); } } bool GuiSharedSizeItemComposition::GetSharedWidth() { return sharedWidth; } void GuiSharedSizeItemComposition::SetSharedWidth(bool value) { if (sharedWidth != value) { sharedWidth = value; Update(); } } bool GuiSharedSizeItemComposition::GetSharedHeight() { return sharedHeight; } void GuiSharedSizeItemComposition::SetSharedHeight(bool value) { if (sharedHeight != value) { sharedHeight = value; Update(); } } /*********************************************************************** GuiSharedSizeRootComposition ***********************************************************************/ void GuiSharedSizeRootComposition::AddSizeComponent(collections::Dictionary& sizes, const WString& group, vint sizeComponent) { vint index = sizes.Keys().IndexOf(group); if (index == -1) { sizes.Add(group, sizeComponent); } else if (sizes.Values().Get(index) < sizeComponent) { sizes.Set(group, sizeComponent); } } void GuiSharedSizeRootComposition::CollectSizes(collections::Dictionary& widths, collections::Dictionary& heights) { FOREACH(GuiSharedSizeItemComposition*, item, childItems) { auto group = item->GetGroup(); auto minSize = item->GetPreferredMinSize(); item->SetPreferredMinSize(Size(0, 0)); auto size = item->GetPreferredBounds().GetSize(); if (item->GetSharedWidth()) { AddSizeComponent(widths, group, size.x); } if (item->GetSharedHeight()) { AddSizeComponent(heights, group, size.y); } item->SetPreferredMinSize(minSize); } } void GuiSharedSizeRootComposition::AlignSizes(collections::Dictionary& widths, collections::Dictionary& heights) { FOREACH(GuiSharedSizeItemComposition*, item, childItems) { auto group = item->GetGroup(); auto size = item->GetPreferredMinSize(); if (item->GetSharedWidth()) { size.x = widths[group]; } if (item->GetSharedHeight()) { size.y = heights[group]; } item->SetPreferredMinSize(size); } } void GuiSharedSizeRootComposition::UpdateBounds() { } GuiSharedSizeRootComposition::GuiSharedSizeRootComposition() { } GuiSharedSizeRootComposition::~GuiSharedSizeRootComposition() { } void GuiSharedSizeRootComposition::ForceCalculateSizeImmediately() { itemWidths.Clear(); itemHeights.Clear(); CollectSizes(itemWidths, itemHeights); AlignSizes(itemWidths, itemHeights); GuiBoundsComposition::ForceCalculateSizeImmediately(); } Rect GuiSharedSizeRootComposition::GetBounds() { Dictionary widths, heights; CollectSizes(widths, heights); bool minSizeModified = CompareEnumerable(itemWidths, widths) != 0 || CompareEnumerable(itemHeights, heights) != 0; if (minSizeModified) { CopyFrom(itemWidths, widths); CopyFrom(itemHeights, heights); AlignSizes(itemWidths, itemHeights); GuiBoundsComposition::ForceCalculateSizeImmediately(); } return GuiBoundsComposition::GetBounds(); } /*********************************************************************** GuiRepeatCompositionBase ***********************************************************************/ void GuiRepeatCompositionBase::OnItemChanged(vint index, vint oldCount, vint newCount) { if (itemTemplate && itemSource) { for (vint i = oldCount - 1; i >= 0; i--) { RemoveItem(index + i); } for (vint i = 0; i < newCount; i++) { InstallItem(index + i); } } } void GuiRepeatCompositionBase::RemoveItem(vint index) { GuiItemEventArgs arguments(dynamic_cast(this)); arguments.itemIndex = index; ItemRemoved.Execute(arguments); auto item = RemoveRepeatComposition(index); SafeDeleteComposition(item); } void GuiRepeatCompositionBase::InstallItem(vint index) { auto source = itemSource->Get(index); auto templateItem = itemTemplate(source); auto item = InsertRepeatComposition(index); templateItem->SetAlignmentToParent(Margin(0, 0, 0, 0)); item->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); item->AddChild(templateItem); GuiItemEventArgs arguments(dynamic_cast(this)); arguments.itemIndex = index; ItemInserted.Execute(arguments); } void GuiRepeatCompositionBase::ClearItems() { for (vint i = GetRepeatCompositionCount() - 1; i >= 0; i--) { RemoveItem(i); } } void GuiRepeatCompositionBase::InstallItems() { if (itemTemplate && itemSource) { vint count = itemSource->GetCount(); for (vint i = 0; i < count; i++) { InstallItem(i); } } } GuiRepeatCompositionBase::GuiRepeatCompositionBase() { } GuiRepeatCompositionBase::~GuiRepeatCompositionBase() { } GuiRepeatCompositionBase::ItemStyleProperty GuiRepeatCompositionBase::GetItemTemplate() { return itemTemplate; } void GuiRepeatCompositionBase::SetItemTemplate(ItemStyleProperty value) { ClearItems(); itemTemplate = value; if (itemTemplate && itemSource) { InstallItems(); } } Ptr GuiRepeatCompositionBase::GetItemSource() { return itemSource; } void GuiRepeatCompositionBase::SetItemSource(Ptr value) { if (value != itemSource) { if (itemChangedHandler) { itemSource.Cast()->ItemChanged.Remove(itemChangedHandler); } ClearItems(); itemSource = value.Cast(); if (!itemSource && value) { itemSource = IValueList::Create(GetLazyList(value)); } if (itemTemplate && itemSource) { InstallItems(); } if (auto observable = itemSource.Cast()) { itemChangedHandler = observable->ItemChanged.Add(this, &GuiRepeatCompositionBase::OnItemChanged); } } } /*********************************************************************** GuiRepeatStackComposition ***********************************************************************/ vint GuiRepeatStackComposition::GetRepeatCompositionCount() { return stackItems.Count(); } GuiGraphicsComposition* GuiRepeatStackComposition::GetRepeatComposition(vint index) { return stackItems[index]; } GuiGraphicsComposition* GuiRepeatStackComposition::InsertRepeatComposition(vint index) { CHECK_ERROR(0 <= index && index <= stackItems.Count(), L"GuiRepeatStackComposition::InsertRepeatComposition(vint)#Index out of range."); auto item = new GuiStackItemComposition; InsertStackItem(index, item); return item; } GuiGraphicsComposition* GuiRepeatStackComposition::RemoveRepeatComposition(vint index) { auto item = stackItems[index]; RemoveChild(item); return item; } /*********************************************************************** GuiRepeatFlowComposition ***********************************************************************/ vint GuiRepeatFlowComposition::GetRepeatCompositionCount() { return flowItems.Count(); } GuiGraphicsComposition* GuiRepeatFlowComposition::GetRepeatComposition(vint index) { return flowItems[index]; } GuiGraphicsComposition* GuiRepeatFlowComposition::InsertRepeatComposition(vint index) { CHECK_ERROR(0 <= index && index <= flowItems.Count(), L"GuiRepeatStackComposition::InsertRepeatComposition(vint)#Index out of range."); auto item = new GuiFlowItemComposition; InsertFlowItem(index, item); return item; } GuiGraphicsComposition* GuiRepeatFlowComposition::RemoveRepeatComposition(vint index) { auto item = flowItems[index]; RemoveChild(item); return item; } } } } /*********************************************************************** .\GRAPHICSCOMPOSITION\GUIGRAPHICSCOMPOSITIONBASE.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace compositions { using namespace collections; using namespace controls; using namespace elements; void InvokeOnCompositionStateChanged(compositions::GuiGraphicsComposition* composition) { composition->InvokeOnCompositionStateChanged(); } /*********************************************************************** GuiGraphicsComposition ***********************************************************************/ void GuiGraphicsComposition::OnControlParentChanged(controls::GuiControl* control) { if(associatedControl && associatedControl!=control) { if(associatedControl->GetParent()) { associatedControl->GetParent()->OnChildRemoved(associatedControl); } if(control) { control->OnChildInserted(associatedControl); } } else { for(vint i=0;iOnControlParentChanged(control); } } } void GuiGraphicsComposition::OnChildInserted(GuiGraphicsComposition* child) { child->OnControlParentChanged(GetRelatedControl()); } void GuiGraphicsComposition::OnChildRemoved(GuiGraphicsComposition* child) { child->OnControlParentChanged(0); } void GuiGraphicsComposition::OnParentChanged(GuiGraphicsComposition* oldParent, GuiGraphicsComposition* newParent) { OnParentLineChanged(); } void GuiGraphicsComposition::OnParentLineChanged() { for (vint i = 0; i < children.Count(); i++) { children[i]->OnParentLineChanged(); } } void GuiGraphicsComposition::OnRenderContextChanged() { } void GuiGraphicsComposition::UpdateRelatedHostRecord(GraphicsHostRecord* record) { relatedHostRecord = record; auto renderTarget = GetRenderTarget(); if (ownedElement) { if (auto renderer = ownedElement->GetRenderer()) { renderer->SetRenderTarget(renderTarget); } } for (vint i = 0; i < children.Count(); i++) { children[i]->UpdateRelatedHostRecord(record); } if (HasEventReceiver()) { GetEventReceiver()->renderTargetChanged.Execute(GuiEventArgs(this)); } if (associatedControl) { associatedControl->OnRenderTargetChanged(renderTarget); } OnRenderContextChanged(); } void GuiGraphicsComposition::SetAssociatedControl(controls::GuiControl* control) { if (associatedControl) { for (vint i = 0; i < children.Count(); i++) { children[i]->OnControlParentChanged(0); } } associatedControl = control; if (associatedControl) { for (vint i = 0; i < children.Count(); i++) { children[i]->OnControlParentChanged(associatedControl); } } } void GuiGraphicsComposition::InvokeOnCompositionStateChanged() { if (relatedHostRecord) { relatedHostRecord->host->RequestRender(); } } bool GuiGraphicsComposition::SharedPtrDestructorProc(DescriptableObject* obj, bool forceDisposing) { GuiGraphicsComposition* value=dynamic_cast(obj); if(value->parent) { if (!forceDisposing) return false; } SafeDeleteComposition(value); return true; } GuiGraphicsComposition::GuiGraphicsComposition() { sharedPtrDestructorProc = &GuiGraphicsComposition::SharedPtrDestructorProc; } GuiGraphicsComposition::~GuiGraphicsComposition() { for(vint i=0;iGetParent()) return false; children.Insert(index, child); // composition parent changed -> control parent changed -> related host changed child->parent = this; child->OnParentChanged(nullptr, this); OnChildInserted(child); child->UpdateRelatedHostRecord(relatedHostRecord); InvokeOnCompositionStateChanged(); return true; } bool GuiGraphicsComposition::RemoveChild(GuiGraphicsComposition* child) { if (!child) return false; vint index = children.IndexOf(child); if (index == -1) return false; // composition parent changed -> control parent changed -> related host changed child->parent = nullptr; child->OnParentChanged(this, nullptr); OnChildRemoved(child); child->UpdateRelatedHostRecord(nullptr); GuiGraphicsHost* host = GetRelatedGraphicsHost(); if (host) { host->DisconnectComposition(child); } children.RemoveAt(index); InvokeOnCompositionStateChanged(); return true; } bool GuiGraphicsComposition::MoveChild(GuiGraphicsComposition* child, vint newIndex) { if(!child) return false; vint index=children.IndexOf(child); if(index==-1) return false; children.RemoveAt(index); children.Insert(newIndex, child); InvokeOnCompositionStateChanged(); return true; } Ptr GuiGraphicsComposition::GetOwnedElement() { return ownedElement; } void GuiGraphicsComposition::SetOwnedElement(Ptr element) { if (ownedElement != element) { if (ownedElement) { if (auto renderer = ownedElement->GetRenderer()) { renderer->SetRenderTarget(nullptr); } ownedElement->SetOwnerComposition(nullptr); } ownedElement = element; if (ownedElement) { if (auto renderer = ownedElement->GetRenderer()) { renderer->SetRenderTarget(GetRenderTarget()); } ownedElement->SetOwnerComposition(this); } InvokeOnCompositionStateChanged(); } } bool GuiGraphicsComposition::GetVisible() { return visible; } void GuiGraphicsComposition::SetVisible(bool value) { visible = value; InvokeOnCompositionStateChanged(); } GuiGraphicsComposition::MinSizeLimitation GuiGraphicsComposition::GetMinSizeLimitation() { return minSizeLimitation; } void GuiGraphicsComposition::SetMinSizeLimitation(MinSizeLimitation value) { minSizeLimitation = value; InvokeOnCompositionStateChanged(); } elements::IGuiGraphicsRenderTarget* GuiGraphicsComposition::GetRenderTarget() { return relatedHostRecord ? relatedHostRecord->renderTarget : nullptr; } void GuiGraphicsComposition::Render(Size offset) { auto renderTarget = GetRenderTarget(); if (visible && renderTarget && !renderTarget->IsClipperCoverWholeTarget()) { Rect bounds = GetBounds(); bounds.x1 += margin.left; bounds.y1 += margin.top; bounds.x2 -= margin.right; bounds.y2 -= margin.bottom; if (bounds.x1 <= bounds.x2 && bounds.y1 <= bounds.y2) { bounds.x1 += offset.x; bounds.x2 += offset.x; bounds.y1 += offset.y; bounds.y2 += offset.y; if (ownedElement) { IGuiGraphicsRenderer* renderer = ownedElement->GetRenderer(); if (renderer) { renderer->Render(bounds); } } if (children.Count() > 0) { bounds.x1 += internalMargin.left; bounds.y1 += internalMargin.top; bounds.x2 -= internalMargin.right; bounds.y2 -= internalMargin.bottom; if (bounds.x1 <= bounds.x2 && bounds.y1 <= bounds.y2) { offset = bounds.GetSize(); renderTarget->PushClipper(bounds); if (!renderTarget->IsClipperCoverWholeTarget()) { for (vint i = 0; i < children.Count(); i++) { children[i]->Render(Size(bounds.x1, bounds.y1)); } } renderTarget->PopClipper(); } } } } } GuiGraphicsEventReceiver* GuiGraphicsComposition::GetEventReceiver() { if(!eventReceiver) { eventReceiver=new GuiGraphicsEventReceiver(this); } return eventReceiver.Obj(); } bool GuiGraphicsComposition::HasEventReceiver() { return eventReceiver; } GuiGraphicsComposition* GuiGraphicsComposition::FindComposition(Point location, bool forMouseEvent) { if (!visible) return 0; Rect bounds = GetBounds(); Rect relativeBounds = Rect(Point(0, 0), bounds.GetSize()); if (relativeBounds.Contains(location)) { Rect clientArea = GetClientArea(); for (vint i = children.Count() - 1; i >= 0; i--) { GuiGraphicsComposition* child = children[i]; Rect childBounds = child->GetBounds(); vint offsetX = childBounds.x1 + (clientArea.x1 - bounds.x1); vint offsetY = childBounds.y1 + (clientArea.y1 - bounds.y1); Point newLocation = location - Size(offsetX, offsetY); GuiGraphicsComposition* childResult = child->FindComposition(newLocation, forMouseEvent); if (childResult) { return childResult; } } if (!forMouseEvent || !transparentToMouse) { return this; } } return nullptr; } bool GuiGraphicsComposition::GetTransparentToMouse() { return transparentToMouse; } void GuiGraphicsComposition::SetTransparentToMouse(bool value) { transparentToMouse = value; } Rect GuiGraphicsComposition::GetGlobalBounds() { Rect bounds = GetBounds(); GuiGraphicsComposition* composition = parent; while (composition) { Rect clientArea = composition->GetClientArea(); Rect parentBounds = composition->GetBounds(); bounds.x1 += clientArea.x1; bounds.x2 += clientArea.x1; bounds.y1 += clientArea.y1; bounds.y2 += clientArea.y1; composition = composition->parent; } return bounds; } controls::GuiControl* GuiGraphicsComposition::GetAssociatedControl() { return associatedControl; } GuiGraphicsHost* GuiGraphicsComposition::GetAssociatedHost() { if (relatedHostRecord && relatedHostRecord->host->GetMainComposition() == this) { return relatedHostRecord->host; } else { return nullptr; } } INativeCursor* GuiGraphicsComposition::GetAssociatedCursor() { return associatedCursor; } void GuiGraphicsComposition::SetAssociatedCursor(INativeCursor* cursor) { associatedCursor = cursor; } INativeWindowListener::HitTestResult GuiGraphicsComposition::GetAssociatedHitTestResult() { return associatedHitTestResult; } void GuiGraphicsComposition::SetAssociatedHitTestResult(INativeWindowListener::HitTestResult value) { associatedHitTestResult = value; } controls::GuiControl* GuiGraphicsComposition::GetRelatedControl() { GuiGraphicsComposition* composition = this; while (composition) { if (composition->GetAssociatedControl()) { return composition->GetAssociatedControl(); } else { composition = composition->GetParent(); } } return nullptr; } GuiGraphicsHost* GuiGraphicsComposition::GetRelatedGraphicsHost() { return relatedHostRecord ? relatedHostRecord->host : nullptr; } controls::GuiControlHost* GuiGraphicsComposition::GetRelatedControlHost() { if (auto control = GetRelatedControl()) { return control->GetRelatedControlHost(); } return nullptr; } INativeCursor* GuiGraphicsComposition::GetRelatedCursor() { GuiGraphicsComposition* composition = this; while (composition) { if (composition->GetAssociatedCursor()) { return composition->GetAssociatedCursor(); } else { composition = composition->GetParent(); } } return nullptr; } Margin GuiGraphicsComposition::GetMargin() { return margin; } void GuiGraphicsComposition::SetMargin(Margin value) { margin = value; InvokeOnCompositionStateChanged(); } Margin GuiGraphicsComposition::GetInternalMargin() { return internalMargin; } void GuiGraphicsComposition::SetInternalMargin(Margin value) { internalMargin = value; InvokeOnCompositionStateChanged(); } Size GuiGraphicsComposition::GetPreferredMinSize() { return preferredMinSize; } void GuiGraphicsComposition::SetPreferredMinSize(Size value) { preferredMinSize = value; InvokeOnCompositionStateChanged(); } Rect GuiGraphicsComposition::GetClientArea() { Rect bounds=GetBounds(); bounds.x1+=margin.left+internalMargin.left; bounds.y1+=margin.top+internalMargin.top; bounds.x2-=margin.right+internalMargin.right; bounds.y2-=margin.bottom+internalMargin.bottom; return bounds; } void GuiGraphicsComposition::ForceCalculateSizeImmediately() { for (vint i = 0; i < children.Count(); i++) { children[i]->ForceCalculateSizeImmediately(); } InvokeOnCompositionStateChanged(); } /*********************************************************************** GuiGraphicsSite ***********************************************************************/ Rect GuiGraphicsSite::GetBoundsInternal(Rect expectedBounds) { Size minSize = GetMinPreferredClientSize(); if (minSize.x < preferredMinSize.x) minSize.x = preferredMinSize.x; if (minSize.y < preferredMinSize.y) minSize.y = preferredMinSize.y; minSize.x += margin.left + margin.right + internalMargin.left + internalMargin.right; minSize.y += margin.top + margin.bottom + internalMargin.top + internalMargin.bottom; vint w = expectedBounds.Width(); vint h = expectedBounds.Height(); if (minSize.x < w) minSize.x = w; if (minSize.y < h) minSize.y = h; return Rect(expectedBounds.LeftTop(), minSize); } void GuiGraphicsSite::UpdatePreviousBounds(Rect bounds) { if (previousBounds != bounds) { previousBounds = bounds; BoundsChanged.Execute(GuiEventArgs(this)); InvokeOnCompositionStateChanged(); } } GuiGraphicsSite::GuiGraphicsSite() { BoundsChanged.SetAssociatedComposition(this); } GuiGraphicsSite::~GuiGraphicsSite() { } bool GuiGraphicsSite::IsSizeAffectParent() { return true; } Size GuiGraphicsSite::GetMinPreferredClientSize() { Size minSize; if (minSizeLimitation != GuiGraphicsComposition::NoLimit) { if (ownedElement) { IGuiGraphicsRenderer* renderer = ownedElement->GetRenderer(); if (renderer) { minSize = renderer->GetMinSize(); } } } if (minSizeLimitation == GuiGraphicsComposition::LimitToElementAndChildren) { vint childCount = Children().Count(); for (vint i = 0; i < childCount; i++) { GuiGraphicsComposition* child = children[i]; if (child->IsSizeAffectParent()) { Rect childBounds = child->GetPreferredBounds(); if (minSize.x < childBounds.x2) minSize.x = childBounds.x2; if (minSize.y < childBounds.y2) minSize.y = childBounds.y2; } } } return minSize; } Rect GuiGraphicsSite::GetPreferredBounds() { return GetBoundsInternal(Rect(Point(0, 0), GetMinPreferredClientSize())); } /*********************************************************************** Helper Functions ***********************************************************************/ void NotifyFinalizeInstance(controls::GuiControl* value) { if (value) { NotifyFinalizeInstance(value->GetBoundsComposition()); } } void NotifyFinalizeInstance(GuiGraphicsComposition* value) { if (value) { bool finalized = false; if (auto root = dynamic_cast(value)) { if (root->IsFinalized()) { finalized = true; } else { root->FinalizeInstance(); } } if (auto control = value->GetAssociatedControl()) { if (auto root = dynamic_cast(control)) { if (root->IsFinalized()) { finalized = true; } else { root->FinalizeInstance(); } } } if (!finalized) { vint count = value->Children().Count(); for (vint i = 0; i < count; i++) { NotifyFinalizeInstance(value->Children()[i]); } } } } void SafeDeleteControlInternal(controls::GuiControl* value) { if(value) { if (value->GetRelatedControlHost() != value) { GuiGraphicsComposition* bounds = value->GetBoundsComposition(); if (bounds->GetParent()) { bounds->GetParent()->RemoveChild(bounds); } } delete value; } } void SafeDeleteCompositionInternal(GuiGraphicsComposition* value) { if (value) { if (value->GetParent()) { value->GetParent()->RemoveChild(value); } if (value->GetAssociatedControl()) { SafeDeleteControlInternal(value->GetAssociatedControl()); } else { for (vint i = value->Children().Count() - 1; i >= 0; i--) { SafeDeleteCompositionInternal(value->Children().Get(i)); } delete value; } } } void SafeDeleteControl(controls::GuiControl* value) { NotifyFinalizeInstance(value); SafeDeleteControlInternal(value); } void SafeDeleteComposition(GuiGraphicsComposition* value) { NotifyFinalizeInstance(value); SafeDeleteCompositionInternal(value); } } } } /*********************************************************************** .\GRAPHICSCOMPOSITION\GUIGRAPHICSRESPONSIVECOMPOSITION.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace compositions { using namespace collections; using namespace controls; /*********************************************************************** GuiResponsiveCompositionBase ***********************************************************************/ void GuiResponsiveCompositionBase::OnParentLineChanged() { GuiBoundsComposition::OnParentLineChanged(); GuiResponsiveCompositionBase* responsive = nullptr; { auto parent = GetParent(); while (parent) { if ((responsive = dynamic_cast(parent))) { break; } parent = parent->GetParent(); } } if (responsiveParent != responsive) { if (responsiveParent) { responsiveParent->OnResponsiveChildRemoved(this); responsiveParent->OnResponsiveChildLevelUpdated(); } responsiveParent = responsive; if (responsiveParent) { responsiveParent->OnResponsiveChildInserted(this); responsiveParent->OnResponsiveChildLevelUpdated(); } } } void GuiResponsiveCompositionBase::OnResponsiveChildInserted(GuiResponsiveCompositionBase* child) { } void GuiResponsiveCompositionBase::OnResponsiveChildRemoved(GuiResponsiveCompositionBase* child) { } void GuiResponsiveCompositionBase::OnResponsiveChildLevelUpdated() { if (responsiveParent) { responsiveParent->OnResponsiveChildLevelUpdated(); } else { InvokeOnCompositionStateChanged(); } } GuiResponsiveCompositionBase::GuiResponsiveCompositionBase() { SetMinSizeLimitation(LimitToElementAndChildren); SetPreferredMinSize(Size(1, 1)); LevelCountChanged.SetAssociatedComposition(this); CurrentLevelChanged.SetAssociatedComposition(this); } GuiResponsiveCompositionBase::~GuiResponsiveCompositionBase() { } ResponsiveDirection GuiResponsiveCompositionBase::GetDirection() { return direction; } void GuiResponsiveCompositionBase::SetDirection(ResponsiveDirection value) { if (direction != value) { direction = value; OnResponsiveChildLevelUpdated(); } } /*********************************************************************** GuiResponsiveSharedCollection ***********************************************************************/ void GuiResponsiveSharedCollection::BeforeInsert(vint index, controls::GuiControl* const& value) { CHECK_ERROR(!value->GetBoundsComposition()->GetParent(), L"GuiResponsiveSharedCollection::BeforeInsert(vint, GuiResponsiveSharedCollection* const&)#Cannot insert a shared control that is currently in use."); } void GuiResponsiveSharedCollection::AfterInsert(vint index, controls::GuiControl* const& value) { view->OnResponsiveChildLevelUpdated(); } void GuiResponsiveSharedCollection::BeforeRemove(vint index, controls::GuiControl* const& value) { CHECK_ERROR(!value->GetBoundsComposition()->GetParent(), L"GuiResponsiveSharedCollection::BeforeRemove(vint, GuiResponsiveSharedCollection* const&)#Cannot remove a shared control that is currently in use."); } void GuiResponsiveSharedCollection::AfterRemove(vint index, vint count) { view->OnResponsiveChildLevelUpdated(); } GuiResponsiveSharedCollection::GuiResponsiveSharedCollection(GuiResponsiveViewComposition* _view) :view(_view) { } GuiResponsiveSharedCollection::~GuiResponsiveSharedCollection() { } /*********************************************************************** GuiResponsiveViewCollection ***********************************************************************/ void GuiResponsiveViewCollection::BeforeInsert(vint index, GuiResponsiveCompositionBase* const& value) { CHECK_ERROR(!value->GetParent(), L"GuiResponsiveViewCollection::BeforeRemove(vint, GuiResponsiveCompositionBase* const&)#Cannot insert a view that is currently in use."); } void GuiResponsiveViewCollection::AfterInsert(vint index, GuiResponsiveCompositionBase* const& value) { if (!view->currentView) { view->skipUpdatingLevels = true; view->currentView = value; view->currentView->SetAlignmentToParent(Margin(0, 0, 0, 0)); view->AddChild(view->currentView); view->skipUpdatingLevels = false; } view->OnResponsiveChildLevelUpdated(); } void GuiResponsiveViewCollection::BeforeRemove(vint index, GuiResponsiveCompositionBase* const& value) { CHECK_ERROR(!value->GetParent(), L"GuiResponsiveViewCollection::BeforeRemove(vint, GuiResponsiveCompositionBase* const&)#Cannot remove a view that is currently in use."); } void GuiResponsiveViewCollection::AfterRemove(vint index, vint count) { view->OnResponsiveChildLevelUpdated(); } GuiResponsiveViewCollection::GuiResponsiveViewCollection(GuiResponsiveViewComposition* _view) :view(_view) { } GuiResponsiveViewCollection::~GuiResponsiveViewCollection() { } /*********************************************************************** GuiResponsiveSharedComposition ***********************************************************************/ void GuiResponsiveSharedComposition::SetSharedControl() { if (shared && view && !view->destructing) { auto sharedParent = shared->GetBoundsComposition()->GetParent(); CHECK_ERROR(view->sharedControls.Contains(shared), L"GuiResponsiveSharedComposition::SetSharedControl()#The specified shared control is not in GuiResponsiveViewComposition::GetSharedControls()."); CHECK_ERROR(!sharedParent || sharedParent == this, L"GuiResponsiveSharedComposition::SetSharedControl()#The specified shared control has not been released. This usually means this control is not in GuiResponsiveViewComposition::GetSharedControls()."); if (!sharedParent) { shared->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); AddChild(shared->GetBoundsComposition()); view->usedSharedControls.Add(shared); } } } void GuiResponsiveSharedComposition::OnParentLineChanged() { GuiBoundsComposition::OnParentLineChanged(); if (view && view->destructing) { return; } GuiResponsiveViewComposition* currentView = nullptr; { auto parent = GetParent(); while (parent) { if ((currentView = dynamic_cast(parent))) { break; } parent = parent->GetParent(); } } if (currentView != view && view && shared) { RemoveChild(shared->GetBoundsComposition()); view->usedSharedControls.Remove(shared); } view = currentView; SetSharedControl(); } GuiResponsiveSharedComposition::GuiResponsiveSharedComposition() { SetMinSizeLimitation(LimitToElementAndChildren); } GuiResponsiveSharedComposition::~GuiResponsiveSharedComposition() { } controls::GuiControl* GuiResponsiveSharedComposition::GetShared() { return shared; } void GuiResponsiveSharedComposition::SetShared(controls::GuiControl* value) { if (shared != value) { CHECK_ERROR(!shared || !shared->GetBoundsComposition()->GetParent(), L"GuiResponsiveSharedComposition::SetShared(GuiControl*)#Cannot replace a shared control that is currently in use."); shared = value; SetSharedControl(); } } /*********************************************************************** GuiResponsiveViewComposition ***********************************************************************/ bool GuiResponsiveViewComposition::CalculateLevelCount() { vint old = levelCount; if (views.Count() == 0) { levelCount = 1; } else { levelCount = 0; for (vint i = 0; i < views.Count(); i++) { auto view = views[i]; if (((vint)direction & (vint)view->GetDirection()) != 0) { levelCount += view->GetLevelCount(); } else { levelCount += 1; } } } if (old != levelCount) { LevelCountChanged.Execute(GuiEventArgs(this)); return true; } return false; } bool GuiResponsiveViewComposition::CalculateCurrentLevel() { vint old = currentLevel; currentLevel = 0; for (vint i = views.Count() - 1; i >= 0; i--) { auto view = views[i]; if (((vint)direction & (vint)view->GetDirection()) != 0) { if (currentView == view) { currentLevel += view->GetCurrentLevel() + 1; break; } else { currentLevel += view->GetLevelCount(); } } else { currentLevel++; } } currentLevel--; if (old != currentLevel) { CurrentLevelChanged.Execute(GuiEventArgs(this)); return true; } return false; } void GuiResponsiveViewComposition::OnResponsiveChildLevelUpdated() { if (!skipUpdatingLevels) { CalculateLevelCount(); CalculateCurrentLevel(); GuiResponsiveCompositionBase::OnResponsiveChildLevelUpdated(); } } GuiResponsiveViewComposition::GuiResponsiveViewComposition() :sharedControls(this) , views(this) { BeforeSwitchingView.SetAssociatedComposition(this); } GuiResponsiveViewComposition::~GuiResponsiveViewComposition() { destructing = true; FOREACH(GuiResponsiveCompositionBase*, view, views) { if (view != currentView) { SafeDeleteComposition(view); } } FOREACH(GuiControl*, shared, From(sharedControls).Except(usedSharedControls)) { SafeDeleteControl(shared); } } vint GuiResponsiveViewComposition::GetLevelCount() { return levelCount; } vint GuiResponsiveViewComposition::GetCurrentLevel() { return currentLevel; } bool GuiResponsiveViewComposition::LevelDown() { skipUpdatingLevels = true; if (((vint)direction & (vint)currentView->GetDirection()) != 0 && !currentView->LevelDown()) { vint index = views.IndexOf(currentView); if (index < views.Count() - 1) { RemoveChild(currentView); currentView = views[index + 1]; currentView->SetAlignmentToParent(Margin(0, 0, 0, 0)); { GuiItemEventArgs arguments(this); arguments.itemIndex = views.IndexOf(currentView); BeforeSwitchingView.Execute(arguments); } AddChild(currentView); } } skipUpdatingLevels = false; auto x = CalculateLevelCount(); auto y = CalculateCurrentLevel(); if (!x && !y) return false; InvokeOnCompositionStateChanged(); return true; } bool GuiResponsiveViewComposition::LevelUp() { skipUpdatingLevels = true; if (((vint)direction & (vint)currentView->GetDirection()) != 0 && !currentView->LevelUp()) { vint index = views.IndexOf(currentView); if (index > 0) { RemoveChild(currentView); currentView = views[index - 1]; currentView->SetAlignmentToParent(Margin(0, 0, 0, 0)); { GuiItemEventArgs arguments(this); arguments.itemIndex = views.IndexOf(currentView); BeforeSwitchingView.Execute(arguments); } AddChild(currentView); } } skipUpdatingLevels = false; auto x = CalculateLevelCount(); auto y = CalculateCurrentLevel(); if (!x && !y) return false; InvokeOnCompositionStateChanged(); return true; } GuiResponsiveCompositionBase* GuiResponsiveViewComposition::GetCurrentView() { return currentView; } collections::ObservableListBase& GuiResponsiveViewComposition::GetSharedControls() { return sharedControls; } collections::ObservableListBase& GuiResponsiveViewComposition::GetViews() { return views; } /*********************************************************************** GuiResponsiveFixedComposition ***********************************************************************/ void GuiResponsiveFixedComposition::OnResponsiveChildLevelUpdated() { InvokeOnCompositionStateChanged(); } GuiResponsiveFixedComposition::GuiResponsiveFixedComposition() { } GuiResponsiveFixedComposition::~GuiResponsiveFixedComposition() { } vint GuiResponsiveFixedComposition::GetLevelCount() { return 1; } vint GuiResponsiveFixedComposition::GetCurrentLevel() { return 0; } bool GuiResponsiveFixedComposition::LevelDown() { return false; } bool GuiResponsiveFixedComposition::LevelUp() { return false; } /*********************************************************************** GuiResponsiveStackComposition ***********************************************************************/ #define DEFINE_AVAILABLE \ auto availables = From(responsiveChildren) \ .Where([=](GuiResponsiveCompositionBase* child) \ { \ return ((vint)direction & (vint)child->GetDirection()) != 0; \ }) \ bool GuiResponsiveStackComposition::CalculateLevelCount() { vint old = levelCount; DEFINE_AVAILABLE; if (availables.IsEmpty()) { levelCount = 1; } else { levelCount = availables .Select([](GuiResponsiveCompositionBase* child) { return child->GetLevelCount() - 1; }) .Aggregate([](vint a, vint b) { return a + b; }) + 1; } if (old != levelCount) { LevelCountChanged.Execute(GuiEventArgs(this)); return true; } return false; } bool GuiResponsiveStackComposition::CalculateCurrentLevel() { vint old = currentLevel; DEFINE_AVAILABLE; if (availables.IsEmpty()) { currentLevel = 0; } else { currentLevel = availables .Select([](GuiResponsiveCompositionBase* child) { return child->GetCurrentLevel(); }) .Aggregate([](vint a, vint b) { return a + b; }); } if (old != currentLevel) { CurrentLevelChanged.Execute(GuiEventArgs(this)); return true; } return false; } void GuiResponsiveStackComposition::OnResponsiveChildInserted(GuiResponsiveCompositionBase* child) { responsiveChildren.Add(child); } void GuiResponsiveStackComposition::OnResponsiveChildRemoved(GuiResponsiveCompositionBase* child) { responsiveChildren.Remove(child); } void GuiResponsiveStackComposition::OnResponsiveChildLevelUpdated() { CalculateLevelCount(); CalculateCurrentLevel(); GuiResponsiveCompositionBase::OnResponsiveChildLevelUpdated(); } bool GuiResponsiveStackComposition::ChangeLevel(bool levelDown) { DEFINE_AVAILABLE; SortedList ignored; while (true) { GuiResponsiveCompositionBase* selected = nullptr; vint size = 0; FOREACH(GuiResponsiveCompositionBase*, child, availables) { if (!ignored.Contains(child)) { Size childSize = child->GetPreferredBounds().GetSize(); vint childSizeToCompare = direction == ResponsiveDirection::Horizontal ? childSize.x : direction == ResponsiveDirection::Vertical ? childSize.y : childSize.x * childSize.y; if (!selected || (levelDown ? size < childSizeToCompare : size > childSizeToCompare)) { selected = child; size = childSizeToCompare; } } } if (!selected) { break; } else if (levelDown ? selected->LevelDown() : selected->LevelUp()) { break; } else { ignored.Add(selected); } } if (!CalculateCurrentLevel()) return false; InvokeOnCompositionStateChanged(); return true; } GuiResponsiveStackComposition::GuiResponsiveStackComposition() { } GuiResponsiveStackComposition::~GuiResponsiveStackComposition() { } vint GuiResponsiveStackComposition::GetLevelCount() { return levelCount; } vint GuiResponsiveStackComposition::GetCurrentLevel() { return currentLevel; } bool GuiResponsiveStackComposition::LevelDown() { return ChangeLevel(true); } bool GuiResponsiveStackComposition::LevelUp() { return ChangeLevel(false); } /*********************************************************************** GuiResponsiveGroupComposition ***********************************************************************/ bool GuiResponsiveGroupComposition::CalculateLevelCount() { vint old = levelCount; DEFINE_AVAILABLE; if (availables.IsEmpty()) { levelCount = 1; } else { levelCount = availables .Select([](GuiResponsiveCompositionBase* child) { return child->GetLevelCount(); }) .Max(); } if (old != levelCount) { LevelCountChanged.Execute(GuiEventArgs(this)); return true; } return false; } bool GuiResponsiveGroupComposition::CalculateCurrentLevel() { vint old = currentLevel; DEFINE_AVAILABLE; if (availables.IsEmpty()) { currentLevel = 0; } else { currentLevel = availables .Select([](GuiResponsiveCompositionBase* child) { return child->GetCurrentLevel(); }) .Max(); } if (old != currentLevel) { CurrentLevelChanged.Execute(GuiEventArgs(this)); return true; } return false; } void GuiResponsiveGroupComposition::OnResponsiveChildInserted(GuiResponsiveCompositionBase* child) { responsiveChildren.Add(child); } void GuiResponsiveGroupComposition::OnResponsiveChildRemoved(GuiResponsiveCompositionBase* child) { responsiveChildren.Remove(child); } void GuiResponsiveGroupComposition::OnResponsiveChildLevelUpdated() { CalculateLevelCount(); CalculateCurrentLevel(); GuiResponsiveCompositionBase::OnResponsiveChildLevelUpdated(); } GuiResponsiveGroupComposition::GuiResponsiveGroupComposition() { } GuiResponsiveGroupComposition::~GuiResponsiveGroupComposition() { } vint GuiResponsiveGroupComposition::GetLevelCount() { return levelCount; } vint GuiResponsiveGroupComposition::GetCurrentLevel() { return currentLevel; } bool GuiResponsiveGroupComposition::LevelDown() { DEFINE_AVAILABLE; vint level = currentLevel; FOREACH(GuiResponsiveCompositionBase*, child, availables) { if (child->GetCurrentLevel() >= level) { if (!child->LevelDown()) { break; } } } if (!CalculateCurrentLevel()) return false; InvokeOnCompositionStateChanged(); return true; } bool GuiResponsiveGroupComposition::LevelUp() { DEFINE_AVAILABLE; vint level = currentLevel; FOREACH(GuiResponsiveCompositionBase*, child, availables) { while (child->GetCurrentLevel() <= level) { if (!child->LevelUp()) { break; } } } if (!CalculateCurrentLevel()) return false; InvokeOnCompositionStateChanged(); return true; } #undef DEFINE_AVAILABLE /*********************************************************************** GuiResponsiveContainerComposition ***********************************************************************/ #define RESPONSIVE_INVALID_SIZE Size(-1, -1) void GuiResponsiveContainerComposition::OnBoundsChanged(GuiGraphicsComposition* sender, GuiEventArgs& arguments) { if (!responsiveTarget) return; const Size containerSize = GetBounds().GetSize(); const Size responsiveOriginalSize = responsiveTarget->GetPreferredBounds().GetSize(); const bool testX = (vint)responsiveTarget->GetDirection() & (vint)ResponsiveDirection::Horizontal; const bool testY = (vint)responsiveTarget->GetDirection() & (vint)ResponsiveDirection::Vertical; #define RESPONSIVE_IF_CONTAINER(OP, SIZE) ((testX && (containerSize).x OP SIZE.x) || (testY && (containerSize).y OP SIZE.y)) if (upperLevelSize != RESPONSIVE_INVALID_SIZE && RESPONSIVE_IF_CONTAINER(>=, upperLevelSize)) { upperLevelSize = RESPONSIVE_INVALID_SIZE; } if (upperLevelSize == RESPONSIVE_INVALID_SIZE && RESPONSIVE_IF_CONTAINER(>=, responsiveOriginalSize)) { while (true) { if (responsiveTarget->GetCurrentLevel() == responsiveTarget->GetLevelCount() - 1) { break; } else if (responsiveTarget->LevelUp()) { responsiveTarget->ForceCalculateSizeImmediately(); auto currentSize = responsiveTarget->GetPreferredBounds().GetSize(); if (RESPONSIVE_IF_CONTAINER(<, currentSize)) { upperLevelSize = currentSize; responsiveTarget->LevelDown(); break; } } else { break; } } } else { while (true) { responsiveTarget->ForceCalculateSizeImmediately(); auto currentSize = responsiveTarget->GetPreferredBounds().GetSize(); if (RESPONSIVE_IF_CONTAINER(>=, currentSize)) { break; } if (responsiveTarget->GetCurrentLevel() == 0) { break; } else if(responsiveTarget->LevelDown()) { upperLevelSize = currentSize; } else { break; } } } #undef RESPONSIVE_IF_CONTAINER } GuiResponsiveContainerComposition::GuiResponsiveContainerComposition() :upperLevelSize(RESPONSIVE_INVALID_SIZE) { BoundsChanged.AttachMethod(this, &GuiResponsiveContainerComposition::OnBoundsChanged); } GuiResponsiveContainerComposition::~GuiResponsiveContainerComposition() { } GuiResponsiveCompositionBase* GuiResponsiveContainerComposition::GetResponsiveTarget() { return responsiveTarget; } void GuiResponsiveContainerComposition::SetResponsiveTarget(GuiResponsiveCompositionBase* value) { if (responsiveTarget != value) { if (responsiveTarget) { RemoveChild(responsiveTarget); } responsiveTarget = value; upperLevelSize = RESPONSIVE_INVALID_SIZE; if (responsiveTarget) { responsiveTarget->SetAlignmentToParent(Margin(0, 0, 0, 0)); while (responsiveTarget->LevelUp()); AddChild(responsiveTarget); GuiEventArgs arguments(this); OnBoundsChanged(this, arguments); } } } #undef RESPONSIVE_INVALID_SIZE } } } /*********************************************************************** .\CONTROLS\GUIAPPLICATION.CPP ***********************************************************************/ extern void GuiMain(); namespace vl { namespace presentation { namespace controls { using namespace collections; using namespace compositions; using namespace theme; using namespace description; /*********************************************************************** GuiApplication ***********************************************************************/ void GuiApplication::InvokeClipboardNotify(compositions::GuiGraphicsComposition* composition, compositions::GuiEventArgs& arguments) { if(composition->HasEventReceiver()) { composition->GetEventReceiver()->clipboardNotify.Execute(arguments); } FOREACH(GuiGraphicsComposition*, subComposition, composition->Children()) { InvokeClipboardNotify(subComposition, arguments); } } void GuiApplication::LeftButtonDown(Point position) { OnMouseDown(position); } void GuiApplication::LeftButtonUp(Point position) { } void GuiApplication::RightButtonDown(Point position) { OnMouseDown(position); } void GuiApplication::RightButtonUp(Point position) { } void GuiApplication::ClipboardUpdated() { for(vint i=0;iGetNotifyEventArguments(); windows[i]->ClipboardUpdated.Execute(arguments); InvokeClipboardNotify(windows[i]->GetBoundsComposition(), arguments); } } GuiApplication::GuiApplication() :locale(Locale::UserDefault()) { GetCurrentController()->CallbackService()->InstallListener(this); } GuiApplication::~GuiApplication() { if(sharedTooltipControl) { delete sharedTooltipControl; sharedTooltipControl=0; } GetCurrentController()->CallbackService()->UninstallListener(this); } INativeWindow* GuiApplication::GetThreadContextNativeWindow(GuiControlHost* controlHost) { return nullptr; } void GuiApplication::RegisterWindow(GuiWindow* window) { windows.Add(window); } void GuiApplication::UnregisterWindow(GuiWindow* window) { windows.Remove(window); } void GuiApplication::RegisterPopupOpened(GuiPopup* popup) { vint index=openingPopups.IndexOf(popup); if(index==-1) { openingPopups.Add(popup); if(openingPopups.Count()==1) { GetCurrentController()->InputService()->StartHookMouse(); } } } void GuiApplication::RegisterPopupClosed(GuiPopup* popup) { if(openingPopups.Remove(popup)) { if(openingPopups.Count()==0) { GetCurrentController()->InputService()->StopHookMouse(); } } } void GuiApplication::OnMouseDown(Point location) { GuiWindow* window=GetWindow(location); for(vint i=0;iMouseClickedOnOtherWindow(window); } } } void GuiApplication::TooltipMouseEnter(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { sharedTooltipHovering=true; } void GuiApplication::TooltipMouseLeave(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { sharedTooltipHovering=false; if(sharedTooltipClosing) { CloseTooltip(); } } Locale GuiApplication::GetLocale() { return locale; } void GuiApplication::SetLocale(Locale value) { if (locale != value) { locale = value; LocaleChanged(); } } void GuiApplication::Run(GuiWindow* _mainWindow) { if (!mainWindow) { mainWindow = _mainWindow; GetCurrentController()->WindowService()->Run(mainWindow->GetNativeWindow()); mainWindow = nullptr; } } GuiWindow* GuiApplication::GetMainWindow() { return mainWindow; } const collections::List& GuiApplication::GetWindows() { return windows; } GuiWindow* GuiApplication::GetWindow(Point location) { INativeWindow* nativeWindow = GetCurrentController()->WindowService()->GetWindow(location); if (nativeWindow) { for (vint i = 0; i < windows.Count(); i++) { GuiWindow* window = windows[i]; if (window->GetNativeWindow() == nativeWindow) { return window; } } } return 0; } void GuiApplication::ShowTooltip(GuiControl* owner, GuiControl* tooltip, vint preferredContentWidth, Point location) { GuiWindow* ownerWindow = dynamic_cast(owner->GetRelatedControlHost()); if (sharedTooltipOwnerWindow != ownerWindow) { delete sharedTooltipControl; sharedTooltipControl = 0; } if(!sharedTooltipControl) { sharedTooltipControl = new GuiTooltip(theme::ThemeName::Tooltip); if (ownerWindow) { if (auto tooltipStyle = ownerWindow->GetControlTemplateObject(true)->GetTooltipTemplate()) { sharedTooltipControl->SetControlTemplate(tooltipStyle); } } sharedTooltipControl->GetBoundsComposition()->GetEventReceiver()->mouseEnter.AttachMethod(this, &GuiApplication::TooltipMouseEnter); sharedTooltipControl->GetBoundsComposition()->GetEventReceiver()->mouseLeave.AttachMethod(this, &GuiApplication::TooltipMouseLeave); } sharedTooltipHovering=false; sharedTooltipClosing=false; sharedTooltipOwnerWindow = ownerWindow; sharedTooltipOwner=owner; sharedTooltipControl->SetTemporaryContentControl(tooltip); sharedTooltipControl->SetPreferredContentWidth(preferredContentWidth); sharedTooltipControl->SetClientSize(Size(10, 10)); sharedTooltipControl->ShowPopup(owner, location); } void GuiApplication::CloseTooltip() { if(sharedTooltipControl) { if(sharedTooltipHovering) { sharedTooltipClosing=true; } else { sharedTooltipClosing=false; sharedTooltipControl->Close(); } } } GuiControl* GuiApplication::GetTooltipOwner() { if(!sharedTooltipControl) return 0; if(!sharedTooltipControl->GetTemporaryContentControl()) return 0; return sharedTooltipOwner; } WString GuiApplication::GetExecutablePath() { return GetCurrentController()->GetExecutablePath(); } WString GuiApplication::GetExecutableFolder() { WString path=GetExecutablePath(); for(vint i=path.Length()-1;i>=0;i--) { if(path[i]==L'\\' || path[i]==L'/') { return path.Sub(0, i+1); } } return L""; } bool GuiApplication::IsInMainThread(GuiControlHost* controlHost) { return GetCurrentController()->AsyncService()->IsInMainThread(GetThreadContextNativeWindow(controlHost)); } void GuiApplication::InvokeAsync(const Func& proc) { GetCurrentController()->AsyncService()->InvokeAsync(proc); } void GuiApplication::InvokeInMainThread(GuiControlHost* controlHost, const Func& proc) { GetCurrentController()->AsyncService()->InvokeInMainThread(GetThreadContextNativeWindow(controlHost), proc); } bool GuiApplication::InvokeInMainThreadAndWait(GuiControlHost* controlHost, const Func& proc, vint milliseconds) { CHECK_ERROR(!IsInMainThread(controlHost), L"GuiApplication::InvokeInMainThreadAndWait(GuiControlHost*, const Func&, vint)#This function cannot be called in UI thread."); return GetCurrentController()->AsyncService()->InvokeInMainThreadAndWait(GetThreadContextNativeWindow(controlHost), proc, milliseconds); } Ptr GuiApplication::DelayExecute(const Func& proc, vint milliseconds) { return GetCurrentController()->AsyncService()->DelayExecute(proc, milliseconds); } Ptr GuiApplication::DelayExecuteInMainThread(const Func& proc, vint milliseconds) { return GetCurrentController()->AsyncService()->DelayExecuteInMainThread(proc, milliseconds); } void GuiApplication::RunGuiTask(GuiControlHost* controlHost, const Func& proc) { if(IsInMainThread(controlHost)) { return proc(); } else { InvokeInMainThreadAndWait(controlHost, [&proc]() { proc(); }); } } /*********************************************************************** GuiPluginManager ***********************************************************************/ class GuiPluginManager : public Object, public IGuiPluginManager { protected: List> plugins; bool loaded; public: GuiPluginManager() :loaded(false) { } ~GuiPluginManager() { Unload(); } void AddPlugin(Ptr plugin)override { CHECK_ERROR(!loaded, L"GuiPluginManager::AddPlugin(Ptr)#Load function has already been executed."); auto name = plugin->GetName(); if (name != L"") { FOREACH(Ptr, plugin, plugins) { CHECK_ERROR(plugin->GetName() != name, L"GuiPluginManager::AddPlugin(Ptr)#Duplicated plugin name."); } } plugins.Add(plugin); } void Load()override { CHECK_ERROR(!loaded, L"GuiPluginManager::AddPlugin(Ptr)#Load function has already been executed."); loaded=true; SortedList loaded; Group loading; Dictionary> pluginsToLoad; FOREACH(Ptr, plugin, plugins) { auto name = plugin->GetName(); pluginsToLoad.Add(name, plugin); List dependencies; plugin->GetDependencies(dependencies); FOREACH(WString, dependency, dependencies) { loading.Add(name, dependency); } } while (pluginsToLoad.Count() > 0) { vint count = pluginsToLoad.Count(); { FOREACH_INDEXER(WString, name, index, pluginsToLoad.Keys()) { if (!loading.Keys().Contains(name)) { for (vint i = loading.Count() - 1; i >= 0; i--) { loading.Remove(loading.Keys()[i], name); } loaded.Add(name); auto plugin = pluginsToLoad.Values()[index]; pluginsToLoad.Remove(name); plugin->Load(); break; } } } if (count == pluginsToLoad.Count()) { WString message; FOREACH(Ptr, plugin, pluginsToLoad.Values()) { message += L"Cannot load plugin \"" + plugin->GetName() + L"\" because part of its dependencies are not ready:"; List dependencies; plugin->GetDependencies(dependencies); bool first = true; FOREACH(WString, dependency, dependencies) { if (!loaded.Contains(dependency)) { message += L" \"" + dependency + L"\";"; } } message += L"\r\n"; } throw Exception(message); } } } void Unload()override { CHECK_ERROR(loaded, L"GuiPluginManager::AddPlugin(Ptr)#Load function has not been executed."); loaded=false; FOREACH(Ptr, plugin, plugins) { plugin->Unload(); } } bool IsLoaded()override { return loaded; } }; /*********************************************************************** Helpers ***********************************************************************/ GuiApplication* application=0; IGuiPluginManager* pluginManager=0; GuiApplication* GetApplication() { return application; } IGuiPluginManager* GetPluginManager() { if(!pluginManager) { pluginManager=new GuiPluginManager; } return pluginManager; } void DestroyPluginManager() { if(pluginManager) { delete pluginManager; pluginManager=0; } } /*********************************************************************** GuiApplicationMain ***********************************************************************/ class UIThreadAsyncScheduler :public Object, public IAsyncScheduler, public Description { public: void Execute(const Func& callback)override { GetApplication()->InvokeInMainThread(GetApplication()->GetMainWindow(), callback); } void ExecuteInBackground(const Func& callback)override { GetApplication()->InvokeAsync(callback); } void DelayExecute(const Func& callback, vint milliseconds)override { GetApplication()->DelayExecuteInMainThread(callback, milliseconds); } }; class OtherThreadAsyncScheduler :public Object, public IAsyncScheduler, public Description { public: void Execute(const Func& callback)override { GetApplication()->InvokeAsync(callback); } void ExecuteInBackground(const Func& callback)override { GetApplication()->InvokeAsync(callback); } void DelayExecute(const Func& callback, vint milliseconds)override { GetApplication()->DelayExecute(callback, milliseconds); } }; void GuiApplicationInitialize() { GetCurrentController()->InputService()->StartTimer(); theme::InitializeTheme(); #ifndef VCZH_DEBUG_NO_REFLECTION GetGlobalTypeManager()->Load(); #endif GetPluginManager()->Load(); { GuiApplication app; application = &app; IAsyncScheduler::RegisterSchedulerForCurrentThread(new UIThreadAsyncScheduler); IAsyncScheduler::RegisterDefaultScheduler(new OtherThreadAsyncScheduler); GuiMain(); IAsyncScheduler::UnregisterDefaultScheduler(); IAsyncScheduler::UnregisterSchedulerForCurrentThread(); } application = nullptr; DestroyPluginManager(); theme::FinalizeTheme(); ThreadLocalStorage::DisposeStorages(); FinalizeGlobalStorage(); #ifndef VCZH_DEBUG_NO_REFLECTION DestroyGlobalTypeManager(); #endif } } } } void GuiApplicationMain() { vl::presentation::controls::GuiApplicationInitialize(); } /*********************************************************************** .\CONTROLS\GUIBASICCONTROLS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace elements; using namespace compositions; using namespace collections; using namespace reflection::description; /*********************************************************************** GuiControl ***********************************************************************/ void GuiControl::BeforeControlTemplateUninstalled() { } void GuiControl::AfterControlTemplateInstalled(bool initialize) { controlTemplateObject->SetText(text); controlTemplateObject->SetFont(font); controlTemplateObject->SetContext(context); controlTemplateObject->SetVisuallyEnabled(isVisuallyEnabled); controlTemplateObject->SetFocusableComposition(focusableComposition); } void GuiControl::CheckAndStoreControlTemplate(templates::GuiControlTemplate* value) { controlTemplateObject = value; } void GuiControl::EnsureControlTemplateExists() { if (!controlTemplateObject) { RebuildControlTemplate(); } } void GuiControl::RebuildControlTemplate() { bool initialize = controlTemplateObject == nullptr; if (controlTemplateObject) { BeforeControlTemplateUninstalled(); containerComposition->GetParent()->RemoveChild(containerComposition); boundsComposition->AddChild(containerComposition); SafeDeleteComposition(controlTemplateObject); controlTemplateObject = nullptr; } if (controlTemplate) { CheckAndStoreControlTemplate(controlTemplate({})); } else { CheckAndStoreControlTemplate(theme::GetCurrentTheme()->CreateStyle(controlThemeName)({})); } if (controlTemplateObject) { controlTemplateObject->SetAlignmentToParent(Margin(0, 0, 0, 0)); containerComposition->GetParent()->RemoveChild(containerComposition); boundsComposition->AddChild(controlTemplateObject); controlTemplateObject->GetContainerComposition()->AddChild(containerComposition); AfterControlTemplateInstalled(initialize); } } void GuiControl::OnChildInserted(GuiControl* control) { GuiControl* oldParent=control->parent; children.Add(control); control->parent=this; control->OnParentChanged(oldParent, control->parent); control->UpdateVisuallyEnabled(); } void GuiControl::OnChildRemoved(GuiControl* control) { GuiControl* oldParent=control->parent; control->parent=0; children.Remove(control); control->OnParentChanged(oldParent, control->parent); } void GuiControl::OnParentChanged(GuiControl* oldParent, GuiControl* newParent) { OnParentLineChanged(); } void GuiControl::OnParentLineChanged() { for(vint i=0;iOnParentLineChanged(); } } void GuiControl::OnRenderTargetChanged(elements::IGuiGraphicsRenderTarget* renderTarget) { RenderTargetChanged.Execute(GetNotifyEventArguments()); } void GuiControl::OnBeforeReleaseGraphicsHost() { for(vint i=0;iOnBeforeReleaseGraphicsHost(); } } void GuiControl::UpdateVisuallyEnabled() { bool newValue = isEnabled && (parent == 0 ? true : parent->GetVisuallyEnabled()); if (isVisuallyEnabled != newValue) { isVisuallyEnabled = newValue; if (controlTemplateObject) { controlTemplateObject->SetVisuallyEnabled(isVisuallyEnabled); } VisuallyEnabledChanged.Execute(GetNotifyEventArguments()); for (vint i = 0; i < children.Count(); i++) { children[i]->UpdateVisuallyEnabled(); } } } void GuiControl::SetFocusableComposition(compositions::GuiGraphicsComposition* value) { if (focusableComposition != value) { focusableComposition = value; if (controlTemplateObject) { controlTemplateObject->SetFocusableComposition(focusableComposition); } } } bool GuiControl::IsAltEnabled() { GuiControl* control = this; while (control) { if (!control->GetVisible() || !control->GetEnabled()) { return false; } control = control->GetParent(); } return true; } bool GuiControl::IsAltAvailable() { return focusableComposition != 0 && alt != L""; } compositions::GuiGraphicsComposition* GuiControl::GetAltComposition() { return boundsComposition; } compositions::IGuiAltActionHost* GuiControl::GetActivatingAltHost() { return activatingAltHost; } void GuiControl::OnActiveAlt() { SetFocus(); } bool GuiControl::SharedPtrDestructorProc(DescriptableObject* obj, bool forceDisposing) { GuiControl* value=dynamic_cast(obj); if(value->GetBoundsComposition()->GetParent()) { if (!forceDisposing) return false; } SafeDeleteControl(value); return true; } GuiControl::GuiControl(theme::ThemeName themeName) :controlThemeName(themeName) { { boundsComposition = new GuiBoundsComposition; boundsComposition->SetAssociatedControl(this); boundsComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); containerComposition = new GuiBoundsComposition; containerComposition->SetTransparentToMouse(true); containerComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); containerComposition->SetAlignmentToParent(Margin(0, 0, 0, 0)); boundsComposition->AddChild(containerComposition); } { ControlThemeNameChanged.SetAssociatedComposition(boundsComposition); ControlTemplateChanged.SetAssociatedComposition(boundsComposition); RenderTargetChanged.SetAssociatedComposition(boundsComposition); VisibleChanged.SetAssociatedComposition(boundsComposition); EnabledChanged.SetAssociatedComposition(boundsComposition); VisuallyEnabledChanged.SetAssociatedComposition(boundsComposition); AltChanged.SetAssociatedComposition(boundsComposition); TextChanged.SetAssociatedComposition(boundsComposition); FontChanged.SetAssociatedComposition(boundsComposition); ContextChanged.SetAssociatedComposition(boundsComposition); } font = GetCurrentController()->ResourceService()->GetDefaultFont(); sharedPtrDestructorProc = &GuiControl::SharedPtrDestructorProc; } GuiControl::~GuiControl() { // prevent a root bounds composition from notifying its dead controls if (!parent) { NotifyFinalizeInstance(boundsComposition); } if (tooltipControl) { // the only legal parent is the GuiApplication::sharedTooltipWindow if (tooltipControl->GetBoundsComposition()->GetParent()) { tooltipControl->GetBoundsComposition()->GetParent()->RemoveChild(tooltipControl->GetBoundsComposition()); } delete tooltipControl; } for (vint i = 0; i < children.Count(); i++) { delete children[i]; } children.Clear(); // let the root control of a control tree delete the whole composition tree if (!parent) { delete boundsComposition; } } compositions::GuiEventArgs GuiControl::GetNotifyEventArguments() { return GuiEventArgs(boundsComposition); } theme::ThemeName GuiControl::GetControlThemeName() { return controlThemeName; } void GuiControl::SetControlThemeName(theme::ThemeName value) { SetControlThemeNameAndTemplate(value, controlTemplate); } GuiControl::ControlTemplatePropertyType GuiControl::GetControlTemplate() { return controlTemplate; } void GuiControl::SetControlTemplate(const ControlTemplatePropertyType& value) { SetControlThemeNameAndTemplate(controlThemeName, value); } void GuiControl::SetControlThemeNameAndTemplate(theme::ThemeName themeNameValue, const ControlTemplatePropertyType& controlTemplateValue) { bool themeChanged = (controlThemeName != themeNameValue); bool templateChanged = (controlTemplate || controlTemplateValue); if (themeChanged || templateChanged) { controlThemeName = themeNameValue; controlTemplate = controlTemplateValue; RebuildControlTemplate(); if (themeChanged) { ControlThemeNameChanged.Execute(GetNotifyEventArguments()); } if (templateChanged) { ControlTemplateChanged.Execute(GetNotifyEventArguments()); } } } templates::GuiControlTemplate* GuiControl::GetControlTemplateObject() { EnsureControlTemplateExists(); return controlTemplateObject; } compositions::GuiBoundsComposition* GuiControl::GetBoundsComposition() { EnsureControlTemplateExists(); return boundsComposition; } compositions::GuiGraphicsComposition* GuiControl::GetContainerComposition() { EnsureControlTemplateExists(); return containerComposition; } compositions::GuiGraphicsComposition* GuiControl::GetFocusableComposition() { EnsureControlTemplateExists(); return focusableComposition; } GuiControl* GuiControl::GetParent() { return parent; } vint GuiControl::GetChildrenCount() { return children.Count(); } GuiControl* GuiControl::GetChild(vint index) { return children[index]; } bool GuiControl::AddChild(GuiControl* control) { return GetContainerComposition()->AddChild(control->GetBoundsComposition()); } bool GuiControl::HasChild(GuiControl* control) { return children.Contains(control); } GuiControlHost* GuiControl::GetRelatedControlHost() { return parent?parent->GetRelatedControlHost():0; } bool GuiControl::GetVisuallyEnabled() { return isVisuallyEnabled; } bool GuiControl::GetEnabled() { return isEnabled; } void GuiControl::SetEnabled(bool value) { if(isEnabled!=value) { isEnabled=value; EnabledChanged.Execute(GetNotifyEventArguments()); UpdateVisuallyEnabled(); } } bool GuiControl::GetVisible() { return isVisible; } void GuiControl::SetVisible(bool value) { boundsComposition->SetVisible(value); if(isVisible!=value) { isVisible=value; VisibleChanged.Execute(GetNotifyEventArguments()); } } const WString& GuiControl::GetAlt() { return alt; } bool GuiControl::SetAlt(const WString& value) { if (!IGuiAltAction::IsLegalAlt(value)) return false; if (alt != value) { alt = value; AltChanged.Execute(GetNotifyEventArguments()); } return true; } void GuiControl::SetActivatingAltHost(compositions::IGuiAltActionHost* host) { activatingAltHost = host; } const WString& GuiControl::GetText() { return text; } void GuiControl::SetText(const WString& value) { if (text != value) { text = value; if (controlTemplateObject) { controlTemplateObject->SetText(text); } TextChanged.Execute(GetNotifyEventArguments()); } } const FontProperties& GuiControl::GetFont() { return font; } void GuiControl::SetFont(const FontProperties& value) { if (font != value) { font = value; if (controlTemplateObject) { controlTemplateObject->SetFont(font); } FontChanged.Execute(GetNotifyEventArguments()); } } description::Value GuiControl::GetContext() { return context; } void GuiControl::SetContext(const description::Value& value) { if (context != value) { context = value; if (controlTemplateObject) { controlTemplateObject->SetContext(context); } ContextChanged.Execute(GetNotifyEventArguments()); } } void GuiControl::SetFocus() { if(focusableComposition) { GuiGraphicsHost* host=focusableComposition->GetRelatedGraphicsHost(); if(host) { host->SetFocus(focusableComposition); } } } description::Value GuiControl::GetTag() { return tag; } void GuiControl::SetTag(const description::Value& value) { tag=value; } GuiControl* GuiControl::GetTooltipControl() { return tooltipControl; } GuiControl* GuiControl::SetTooltipControl(GuiControl* value) { GuiControl* oldTooltipControl=tooltipControl; tooltipControl=value; return oldTooltipControl; } vint GuiControl::GetTooltipWidth() { return tooltipWidth; } void GuiControl::SetTooltipWidth(vint value) { tooltipWidth=value; } bool GuiControl::DisplayTooltip(Point location) { if(!tooltipControl) return false; GetApplication()->ShowTooltip(this, tooltipControl, tooltipWidth, location); return true; } void GuiControl::CloseTooltip() { if(GetApplication()->GetTooltipOwner()==this) { GetApplication()->CloseTooltip(); } } IDescriptable* GuiControl::QueryService(const WString& identifier) { if (identifier == IGuiAltAction::Identifier) { return (IGuiAltAction*)this; } else if (identifier == IGuiAltActionContainer::Identifier) { return 0; } else if(parent) { return parent->QueryService(identifier); } else { return 0; } } /*********************************************************************** GuiCustomControl ***********************************************************************/ controls::GuiControlHost* GuiCustomControl::GetControlHostForInstance() { return GetRelatedControlHost(); } void GuiCustomControl::OnParentLineChanged() { GuiControl::OnParentLineChanged(); OnControlHostForInstanceChanged(); } GuiCustomControl::GuiCustomControl(theme::ThemeName themeName) :GuiControl(themeName) { } GuiCustomControl::~GuiCustomControl() { FinalizeAggregation(); FinalizeInstanceRecursively(this); } } } } /*********************************************************************** .\CONTROLS\TEMPLATES\GUICONTROLSHARED.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace reflection::description; using namespace compositions; /*********************************************************************** GuiComponent ***********************************************************************/ GuiComponent::GuiComponent() { } GuiComponent::~GuiComponent() { } void GuiComponent::Attach(GuiInstanceRootObject* rootObject) { } void GuiComponent::Detach(GuiInstanceRootObject* rootObject) { } /*********************************************************************** GuiInstanceRootObject ***********************************************************************/ class RootObjectTimerCallback : public Object, public IGuiGraphicsTimerCallback { public: GuiControlHost* controlHost; GuiInstanceRootObject* rootObject; bool alive = true; RootObjectTimerCallback(GuiInstanceRootObject* _rootObject, GuiControlHost* _controlHost) :rootObject(_rootObject) , controlHost(_controlHost) { } bool Play()override { if (alive) { for (vint i = rootObject->runningAnimations.Count() - 1; i >= 0; i--) { auto animation = rootObject->runningAnimations[i]; animation->Run(); if (animation->GetStopped()) { rootObject->runningAnimations.RemoveAt(i); } } if (rootObject->runningAnimations.Count() == 0) { rootObject->UninstallTimerCallback(nullptr); return false; } } return alive; } }; void GuiInstanceRootObject::InstallTimerCallback(controls::GuiControlHost* controlHost) { if (!timerCallback) { timerCallback = new RootObjectTimerCallback(this, controlHost); controlHost->GetTimerManager()->AddCallback(timerCallback); } } bool GuiInstanceRootObject::UninstallTimerCallback(controls::GuiControlHost* controlHost) { if (timerCallback && timerCallback->controlHost != controlHost) { timerCallback->alive = false; timerCallback = nullptr; return true; } return false; } void GuiInstanceRootObject::OnControlHostForInstanceChanged() { auto controlHost = GetControlHostForInstance(); if (UninstallTimerCallback(controlHost)) { FOREACH(Ptr, animation, runningAnimations) { animation->Pause(); } } if (controlHost) { InstallTimerCallback(controlHost); FOREACH(Ptr, animation, runningAnimations) { animation->Resume(); } StartPendingAnimations(); } } void GuiInstanceRootObject::StartPendingAnimations() { FOREACH(Ptr, animation, pendingAnimations) { animation->Start(); } CopyFrom(runningAnimations, pendingAnimations, true); pendingAnimations.Clear(); } GuiInstanceRootObject::GuiInstanceRootObject() { } GuiInstanceRootObject::~GuiInstanceRootObject() { UninstallTimerCallback(nullptr); } void GuiInstanceRootObject::FinalizeInstance() { if (!finalized) { finalized = true; FOREACH(Ptr, subscription, subscriptions) { subscription->Close(); } FOREACH(GuiComponent*, component, components) { component->Detach(this); } subscriptions.Clear(); for (vint i = 0; i resolver) { resourceResolver = resolver; } Ptr GuiInstanceRootObject::ResolveResource(const WString& protocol, const WString& path, bool ensureExist) { Ptr object; if (resourceResolver) { object = resourceResolver->ResolveResource(protocol, path); } if (ensureExist && !object) { throw ArgumentException(L"Resource \"" + protocol + L"://" + path + L"\" does not exist."); } return object; } Ptr GuiInstanceRootObject::AddSubscription(Ptr subscription) { CHECK_ERROR(finalized == false, L"GuiInstanceRootObject::AddSubscription(Ptr)#Cannot add subscription after finalizing."); if (subscriptions.Contains(subscription.Obj())) { return nullptr; } else { subscriptions.Add(subscription); subscription->Open(); subscription->Update(); return subscription; } } void GuiInstanceRootObject::UpdateSubscriptions() { FOREACH(Ptr, subscription, subscriptions) { subscription->Update(); } } bool GuiInstanceRootObject::AddComponent(GuiComponent* component) { CHECK_ERROR(finalized == false, L"GuiInstanceRootObject::AddComponent(GuiComponent*)#Cannot add component after finalizing."); if(components.Contains(component)) { return false; } else { components.Add(component); component->Attach(this); return true; } } bool GuiInstanceRootObject::AddControlHostComponent(GuiControlHost* controlHost) { return AddComponent(new GuiObjectComponent(controlHost)); } bool GuiInstanceRootObject::AddAnimation(Ptr animation) { CHECK_ERROR(finalized == false, L"GuiInstanceRootObject::AddAnimation(Ptr)#Cannot add animation after finalizing."); if (runningAnimations.Contains(animation.Obj()) || pendingAnimations.Contains(animation.Obj())) { return false; } else { pendingAnimations.Add(animation); if (auto controlHost = GetControlHostForInstance()) { InstallTimerCallback(controlHost); StartPendingAnimations(); } return true; } } bool GuiInstanceRootObject::KillAnimation(Ptr animation) { if (!animation) return false; if (runningAnimations.Contains(animation.Obj())) { runningAnimations.Remove(animation.Obj()); return true; } if (pendingAnimations.Contains(animation.Obj())) { pendingAnimations.Remove(animation.Obj()); return true; } return false; } } } } /*********************************************************************** .\CONTROLS\TEMPLATES\GUITHEMESTYLEFACTORY.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace theme { using namespace collections; using namespace controls; using namespace templates; class Theme : public Object, public virtual theme::ITheme { public: Dictionary> templates; ThemeTemplates* first = nullptr; ThemeTemplates* last = nullptr; bool RegisterTheme(const WString& name, Ptr theme) { CHECK_ERROR(theme->previous == nullptr, L"vl::presentation::theme::RegisterTheme(const WString&, Ptr)#Theme object has been registered"); CHECK_ERROR(theme->next == nullptr, L"vl::presentation::theme::RegisterTheme(const WString&, Ptr)#Theme object has been registered"); if (templates.Keys().Contains(name)) { return false; } templates.Add(name, theme); if (last) { last->next = theme.Obj(); } theme->previous = last; last = theme.Obj(); return true; } Ptr UnregisterTheme(const WString& name) { vint index = templates.Keys().IndexOf(name); if (index == -1) { return nullptr; } auto themeTemplates = templates.Values().Get(index); if (themeTemplates->previous) { themeTemplates->previous->next = themeTemplates->next; } else { first = themeTemplates->next; } if (themeTemplates->next) { themeTemplates->next->previous = themeTemplates->previous; } else { last = themeTemplates->previous; } templates.Remove(name); return themeTemplates; } TemplateProperty CreateStyle(ThemeName themeName)override { switch (themeName) { #define GUI_DEFINE_ITEM_PROPERTY(TEMPLATE, CONTROL) \ case ThemeName::CONTROL:\ {\ auto current = last;\ while (current) \ {\ if (current->CONTROL)\ {\ return current->CONTROL; \ }\ current = current->previous;\ }\ throw Exception(L"Control template for \"" L ## #CONTROL L"\" is not defined.");\ }\ GUI_CONTROL_TEMPLATE_TYPES(GUI_DEFINE_ITEM_PROPERTY) #undef GUI_DEFINE_ITEM_PROPERTY default: CHECK_FAIL(L"vl::presentation::theme::ITheme::CreateStyle(ThemeName)#Unknown theme name."); } } }; controls::GuiControlHost* ThemeTemplates::GetControlHostForInstance() { return nullptr; } ThemeTemplates::~ThemeTemplates() { FinalizeAggregation(); } Theme* currentTheme = nullptr; ITheme* GetCurrentTheme() { return currentTheme; } void InitializeTheme() { CHECK_ERROR(currentTheme == nullptr, L"vl::presentation::theme::InitializeTheme()#Theme has already been initialized"); currentTheme = new Theme; } void FinalizeTheme() { CHECK_ERROR(currentTheme != nullptr, L"vl::presentation::theme::FinalizeTheme()#Theme has not been initialized"); delete currentTheme; currentTheme = nullptr; } bool RegisterTheme(const WString& name, Ptr theme) { CHECK_ERROR(currentTheme != nullptr, L"vl::presentation::theme::RegisterTheme(const WString&, Ptr)#Theme has already been initialized"); return currentTheme->RegisterTheme(name, theme); } Ptr UnregisterTheme(const WString& name) { CHECK_ERROR(currentTheme != nullptr, L"vl::presentation::theme::UnregisterTheme(const WString&)#Theme has already been initialized"); return currentTheme->UnregisterTheme(name); } } } } /*********************************************************************** .\GRAPHICSELEMENT\GUIGRAPHICSHOST.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace compositions { using namespace collections; using namespace controls; using namespace elements; using namespace theme; /*********************************************************************** GuiGraphicsTimerManager ***********************************************************************/ GuiGraphicsTimerManager::GuiGraphicsTimerManager() { } GuiGraphicsTimerManager::~GuiGraphicsTimerManager() { } void GuiGraphicsTimerManager::AddCallback(Ptr callback) { callbacks.Add(callback); } void GuiGraphicsTimerManager::Play() { for (vint i = callbacks.Count() - 1; i >= 0; i--) { auto callback = callbacks[i]; if (!callback->Play()) { callbacks.RemoveAt(i); } } } /*********************************************************************** IGuiAltAction ***********************************************************************/ const wchar_t* const IGuiAltAction::Identifier = L"vl::presentation::compositions::IGuiAltAction"; const wchar_t* const IGuiAltActionContainer::Identifier = L"vl::presentation::compositions::IGuiAltAction"; const wchar_t* const IGuiAltActionHost::Identifier = L"vl::presentation::compositions::IGuiAltAction"; bool IGuiAltAction::IsLegalAlt(const WString& alt) { for (vint i = 0; i < alt.Length(); i++) { if (alt[i] < L'A' || L'Z' < alt[i]) { return false; } } return true; } void IGuiAltActionHost::CollectAltActionsFromControl(controls::GuiControl* control, collections::Group& actions) { List controls; controls.Add(control); vint current = 0; while (current < controls.Count()) { GuiControl* control = controls[current++]; if (auto container = control->QueryTypedService()) { vint count = container->GetAltActionCount(); for (vint i = 0; i < count; i++) { auto action = container->GetAltAction(i); actions.Add(action->GetAlt(), action); } continue; } else if (auto action = control->QueryTypedService()) { if (action->IsAltAvailable()) { if (action->IsAltEnabled()) { actions.Add(action->GetAlt(), action); continue; } } } vint count = control->GetChildrenCount(); for (vint i = 0; i < count; i++) { controls.Add(control->GetChild(i)); } } } /*********************************************************************** GuiGraphicsHost ***********************************************************************/ void GuiGraphicsHost::EnterAltHost(IGuiAltActionHost* host) { ClearAltHost(); Group actions; host->CollectAltActions(actions); if (actions.Count() == 0) { CloseAltHost(); return; } host->OnActivatedAltHost(currentAltHost); currentAltHost = host; CreateAltTitles(actions); } void GuiGraphicsHost::LeaveAltHost() { if (currentAltHost) { ClearAltHost(); auto previousHost = currentAltHost->GetPreviousAltHost(); currentAltHost->OnDeactivatedAltHost(); currentAltHost = previousHost; if (currentAltHost) { Group actions; currentAltHost->CollectAltActions(actions); CreateAltTitles(actions); } } } bool GuiGraphicsHost::EnterAltKey(wchar_t key) { currentAltPrefix += key; vint index = currentActiveAltActions.Keys().IndexOf(currentAltPrefix); if (index == -1) { if (FilterTitles() == 0) { currentAltPrefix = currentAltPrefix.Left(currentAltPrefix.Length() - 1); FilterTitles(); } } else { auto action = currentActiveAltActions.Values()[index]; if (action->GetActivatingAltHost()) { EnterAltHost(action->GetActivatingAltHost()); } else { CloseAltHost(); } action->OnActiveAlt(); return true; } return false; } void GuiGraphicsHost::LeaveAltKey() { if (currentAltPrefix.Length() >= 1) { currentAltPrefix = currentAltPrefix.Left(currentAltPrefix.Length() - 1); } FilterTitles(); } void GuiGraphicsHost::CreateAltTitles(const collections::Group& actions) { if (currentAltHost) { vint count = actions.Count(); for (vint i = 0; i < count; i++) { WString key = actions.Keys()[i]; const auto& values = actions.GetByIndex(i); vint numberLength = 0; if (values.Count() == 1 && key.Length() > 0) { numberLength = 0; } else if (values.Count() <= 10) { numberLength = 1; } else if (values.Count() <= 100) { numberLength = 2; } else if (values.Count() <= 1000) { numberLength = 3; } else { continue; } FOREACH_INDEXER(IGuiAltAction*, action, index, values) { WString key = actions.Keys()[i]; if (numberLength > 0) { WString number = itow(index); while (number.Length() < numberLength) { number = L"0" + number; } key += number; } currentActiveAltActions.Add(key, action); } } count = currentActiveAltActions.Count(); auto window = dynamic_cast(currentAltHost->GetAltComposition()->GetRelatedControlHost()); for (vint i = 0; i < count; i++) { auto key = currentActiveAltActions.Keys()[i]; auto composition = currentActiveAltActions.Values()[i]->GetAltComposition(); auto label = new GuiLabel(theme::ThemeName::ShortcutKey); if (auto labelStyle = window->GetControlTemplateObject(true)->GetShortcutKeyTemplate()) { label->SetControlTemplate(labelStyle); } label->SetText(key); composition->AddChild(label->GetBoundsComposition()); currentActiveAltTitles.Add(key, label); } FilterTitles(); } } vint GuiGraphicsHost::FilterTitles() { vint count = currentActiveAltTitles.Count(); vint visibles = 0; for (vint i = 0; i < count; i++) { auto key = currentActiveAltTitles.Keys()[i]; auto value = currentActiveAltTitles.Values()[i]; if (key.Length() >= currentAltPrefix.Length() && key.Left(currentAltPrefix.Length()) == currentAltPrefix) { value->SetVisible(true); if (currentAltPrefix.Length() <= key.Length()) { value->SetText( key .Insert(currentAltPrefix.Length(), L"[") .Insert(currentAltPrefix.Length() + 2, L"]") ); } else { value->SetText(key); } visibles++; } else { value->SetVisible(false); } } return visibles; } void GuiGraphicsHost::ClearAltHost() { FOREACH(GuiControl*, title, currentActiveAltTitles.Values()) { SafeDeleteControl(title); } currentActiveAltActions.Clear(); currentActiveAltTitles.Clear(); currentAltPrefix = L""; } void GuiGraphicsHost::CloseAltHost() { ClearAltHost(); while (currentAltHost) { currentAltHost->OnDeactivatedAltHost(); currentAltHost = currentAltHost->GetPreviousAltHost(); } } void GuiGraphicsHost::RefreshRelatedHostRecord(INativeWindow* nativeWindow) { hostRecord.nativeWindow = nativeWindow; hostRecord.renderTarget = nativeWindow ? GetGuiGraphicsResourceManager()->GetRenderTarget(nativeWindow) : nullptr; windowComposition->UpdateRelatedHostRecord(&hostRecord); } void GuiGraphicsHost::DisconnectCompositionInternal(GuiGraphicsComposition* composition) { for(vint i=0;iChildren().Count();i++) { DisconnectCompositionInternal(composition->Children().Get(i)); } if(mouseCaptureComposition==composition) { if(hostRecord.nativeWindow) { hostRecord.nativeWindow->ReleaseCapture(); } mouseCaptureComposition=0; } if(focusedComposition==composition) { focusedComposition=0; } mouseEnterCompositions.Remove(composition); } void GuiGraphicsHost::MouseCapture(const NativeWindowMouseInfo& info) { if(hostRecord.nativeWindow && (info.left || info.middle || info.right)) { if(!hostRecord.nativeWindow->IsCapturing() && !info.nonClient) { hostRecord.nativeWindow->RequireCapture(); mouseCaptureComposition=windowComposition->FindComposition(Point(info.x, info.y), true); } } } void GuiGraphicsHost::MouseUncapture(const NativeWindowMouseInfo& info) { if(hostRecord.nativeWindow && !(info.left || info.middle || info.right)) { hostRecord.nativeWindow->ReleaseCapture(); mouseCaptureComposition=0; } } void GuiGraphicsHost::OnCharInput(const NativeWindowCharInfo& info, GuiGraphicsComposition* composition, GuiCharEvent GuiGraphicsEventReceiver::* eventReceiverEvent) { List compositions; while(composition) { if(composition->HasEventReceiver()) { compositions.Add(composition); } composition=composition->GetParent(); } GuiCharEventArgs arguments(composition); (NativeWindowCharInfo&)arguments=info; for(vint i=compositions.Count()-1;i>=0;i--) { compositions[i]->GetEventReceiver()->previewCharInput.Execute(arguments); if(arguments.handled) { return; } } for(vint i=0;iGetEventReceiver()->*eventReceiverEvent).Execute(arguments); if(arguments.handled) { return; } } } void GuiGraphicsHost::OnKeyInput(const NativeWindowKeyInfo& info, GuiGraphicsComposition* composition, GuiKeyEvent GuiGraphicsEventReceiver::* eventReceiverEvent) { List compositions; while(composition) { if(composition->HasEventReceiver()) { compositions.Add(composition); } composition=composition->GetParent(); } GuiKeyEventArgs arguments(composition); (NativeWindowKeyInfo&)arguments=info; for(vint i=compositions.Count()-1;i>=0;i--) { compositions[i]->GetEventReceiver()->previewKey.Execute(arguments); if(arguments.handled) { return; } } for(vint i=0;iGetEventReceiver()->*eventReceiverEvent).Execute(arguments); if(arguments.handled) { return; } } } void GuiGraphicsHost::RaiseMouseEvent(GuiMouseEventArgs& arguments, GuiGraphicsComposition* composition, GuiMouseEvent GuiGraphicsEventReceiver::* eventReceiverEvent) { arguments.compositionSource=composition; arguments.eventSource=0; vint x=arguments.x; vint y=arguments.y; while(composition) { if(composition->HasEventReceiver()) { if(!arguments.eventSource) { arguments.eventSource=composition; } GuiGraphicsEventReceiver* eventReceiver=composition->GetEventReceiver(); (eventReceiver->*eventReceiverEvent).Execute(arguments); if(arguments.handled) { break; } } GuiGraphicsComposition* parent=composition->GetParent(); if(parent) { Rect parentBounds=parent->GetBounds(); Rect clientArea=parent->GetClientArea(); Rect childBounds=composition->GetBounds(); x+=childBounds.x1+(clientArea.x1-parentBounds.x1); y+=childBounds.y1+(clientArea.y1-parentBounds.y1); arguments.x=x; arguments.y=y; } composition=parent; } } void GuiGraphicsHost::OnMouseInput(const NativeWindowMouseInfo& info, GuiMouseEvent GuiGraphicsEventReceiver::* eventReceiverEvent) { GuiGraphicsComposition* composition=0; if(mouseCaptureComposition) { composition=mouseCaptureComposition; } else { composition=windowComposition->FindComposition(Point(info.x, info.y), true); } if(composition) { Rect bounds=composition->GetGlobalBounds(); GuiMouseEventArgs arguments; (NativeWindowMouseInfo&)arguments=info; arguments.x-=bounds.x1; arguments.y-=bounds.y1; RaiseMouseEvent(arguments, composition, eventReceiverEvent); } } INativeWindowListener::HitTestResult GuiGraphicsHost::HitTest(Point location) { Rect bounds = hostRecord.nativeWindow->GetBounds(); Rect clientBounds = hostRecord.nativeWindow->GetClientBoundsInScreen(); Point clientLocation(location.x + bounds.x1 - clientBounds.x1, location.y + bounds.y1 - clientBounds.y1); GuiGraphicsComposition* hitComposition = windowComposition->FindComposition(clientLocation, false); while (hitComposition) { INativeWindowListener::HitTestResult result = hitComposition->GetAssociatedHitTestResult(); if (result == INativeWindowListener::NoDecision) { hitComposition = hitComposition->GetParent(); } else { return result; } } return INativeWindowListener::NoDecision; } void GuiGraphicsHost::Moving(Rect& bounds, bool fixSizeOnly) { Rect oldBounds = hostRecord.nativeWindow->GetBounds(); minSize = windowComposition->GetPreferredBounds().GetSize(); Size minWindowSize = minSize + (oldBounds.GetSize() - hostRecord.nativeWindow->GetClientSize()); if (bounds.Width() < minWindowSize.x) { if (fixSizeOnly) { if (bounds.Width() < minWindowSize.x) { bounds.x2 = bounds.x1 + minWindowSize.x; } } else if (oldBounds.x1 != bounds.x1) { bounds.x1 = oldBounds.x2 - minWindowSize.x; } else if (oldBounds.x2 != bounds.x2) { bounds.x2 = oldBounds.x1 + minWindowSize.x; } } if (bounds.Height() < minWindowSize.y) { if (fixSizeOnly) { if (bounds.Height() < minWindowSize.y) { bounds.y2 = bounds.y1 + minWindowSize.y; } } else if (oldBounds.y1 != bounds.y1) { bounds.y1 = oldBounds.y2 - minWindowSize.y; } else if (oldBounds.y2 != bounds.y2) { bounds.y2 = oldBounds.y1 + minWindowSize.y; } } } void GuiGraphicsHost::Moved() { Size size = hostRecord.nativeWindow->GetClientSize(); if (previousClientSize != size) { previousClientSize = size; minSize = windowComposition->GetPreferredBounds().GetSize(); needRender = true; } } void GuiGraphicsHost::Paint() { if (!supressPaint) { needRender = true; } } void GuiGraphicsHost::LeftButtonDown(const NativeWindowMouseInfo& info) { CloseAltHost(); MouseCapture(info); OnMouseInput(info, &GuiGraphicsEventReceiver::leftButtonDown); } void GuiGraphicsHost::LeftButtonUp(const NativeWindowMouseInfo& info) { OnMouseInput(info, &GuiGraphicsEventReceiver::leftButtonUp); MouseUncapture(info); } void GuiGraphicsHost::LeftButtonDoubleClick(const NativeWindowMouseInfo& info) { LeftButtonDown(info); OnMouseInput(info, &GuiGraphicsEventReceiver::leftButtonDoubleClick); } void GuiGraphicsHost::RightButtonDown(const NativeWindowMouseInfo& info) { CloseAltHost(); MouseCapture(info); OnMouseInput(info, &GuiGraphicsEventReceiver::rightButtonDown); } void GuiGraphicsHost::RightButtonUp(const NativeWindowMouseInfo& info) { OnMouseInput(info, &GuiGraphicsEventReceiver::rightButtonUp); MouseUncapture(info); } void GuiGraphicsHost::RightButtonDoubleClick(const NativeWindowMouseInfo& info) { RightButtonDown(info); OnMouseInput(info, &GuiGraphicsEventReceiver::rightButtonDoubleClick); } void GuiGraphicsHost::MiddleButtonDown(const NativeWindowMouseInfo& info) { CloseAltHost(); MouseCapture(info); OnMouseInput(info, &GuiGraphicsEventReceiver::middleButtonDown); } void GuiGraphicsHost::MiddleButtonUp(const NativeWindowMouseInfo& info) { OnMouseInput(info, &GuiGraphicsEventReceiver::middleButtonUp); MouseUncapture(info); } void GuiGraphicsHost::MiddleButtonDoubleClick(const NativeWindowMouseInfo& info) { MiddleButtonDown(info); OnMouseInput(info, &GuiGraphicsEventReceiver::middleButtonDoubleClick); } void GuiGraphicsHost::HorizontalWheel(const NativeWindowMouseInfo& info) { OnMouseInput(info, &GuiGraphicsEventReceiver::horizontalWheel); } void GuiGraphicsHost::VerticalWheel(const NativeWindowMouseInfo& info) { OnMouseInput(info, &GuiGraphicsEventReceiver::verticalWheel); } void GuiGraphicsHost::MouseMoving(const NativeWindowMouseInfo& info) { CompositionList newCompositions; { GuiGraphicsComposition* composition = windowComposition->FindComposition(Point(info.x, info.y), true); while (composition) { newCompositions.Insert(0, composition); composition = composition->GetParent(); } } vint firstDifferentIndex = mouseEnterCompositions.Count(); for (vint i = 0; i < mouseEnterCompositions.Count(); i++) { if (i == newCompositions.Count()) { firstDifferentIndex = newCompositions.Count(); break; } if (mouseEnterCompositions[i] != newCompositions[i]) { firstDifferentIndex = i; break; } } for (vint i = mouseEnterCompositions.Count() - 1; i >= firstDifferentIndex; i--) { GuiGraphicsComposition* composition = mouseEnterCompositions[i]; if (composition->HasEventReceiver()) { composition->GetEventReceiver()->mouseLeave.Execute(GuiEventArgs(composition)); } } CopyFrom(mouseEnterCompositions, newCompositions); for (vint i = firstDifferentIndex; i < mouseEnterCompositions.Count(); i++) { GuiGraphicsComposition* composition = mouseEnterCompositions[i]; if (composition->HasEventReceiver()) { composition->GetEventReceiver()->mouseEnter.Execute(GuiEventArgs(composition)); } } INativeCursor* cursor = 0; if (newCompositions.Count() > 0) { cursor = newCompositions[newCompositions.Count() - 1]->GetRelatedCursor(); } if (cursor) { hostRecord.nativeWindow->SetWindowCursor(cursor); } else { hostRecord.nativeWindow->SetWindowCursor(GetCurrentController()->ResourceService()->GetDefaultSystemCursor()); } OnMouseInput(info, &GuiGraphicsEventReceiver::mouseMove); } void GuiGraphicsHost::MouseEntered() { } void GuiGraphicsHost::MouseLeaved() { for(vint i=mouseEnterCompositions.Count()-1;i>=0;i--) { GuiGraphicsComposition* composition=mouseEnterCompositions[i]; if(composition->HasEventReceiver()) { composition->GetEventReceiver()->mouseLeave.Execute(GuiEventArgs(composition)); } } mouseEnterCompositions.Clear(); } void GuiGraphicsHost::KeyDown(const NativeWindowKeyInfo& info) { if (!info.ctrl && !info.shift && currentAltHost) { if (info.code == VKEY_ESCAPE) { LeaveAltHost(); return; } else if (info.code == VKEY_BACK) { LeaveAltKey(); } else if (VKEY_NUMPAD0 <= info.code && info.code <= VKEY_NUMPAD9) { if (EnterAltKey((wchar_t)(L'0' + (info.code - VKEY_NUMPAD0)))) { supressAltKey = info.code; return; } } else if (('0' <= info.code && info.code <= '9') || ('A' <= info.code && info.code <= 'Z')) { if (EnterAltKey((wchar_t)info.code)) { supressAltKey = info.code; return; } } } if (currentAltHost) { return; } if(shortcutKeyManager && shortcutKeyManager->Execute(info)) { return; } if(focusedComposition && focusedComposition->HasEventReceiver()) { OnKeyInput(info, focusedComposition, &GuiGraphicsEventReceiver::keyDown); } } void GuiGraphicsHost::KeyUp(const NativeWindowKeyInfo& info) { if (!info.ctrl && !info.shift && info.code == supressAltKey) { supressAltKey = 0; return; } if(focusedComposition && focusedComposition->HasEventReceiver()) { OnKeyInput(info, focusedComposition, &GuiGraphicsEventReceiver::keyUp); } } void GuiGraphicsHost::SysKeyDown(const NativeWindowKeyInfo& info) { if (!info.ctrl && !info.shift && info.code == VKEY_MENU && !currentAltHost) { if (auto window = dynamic_cast(windowComposition->Children()[0]->GetRelatedControlHost())) { if (auto altHost = window->QueryTypedService()) { if (!altHost->GetPreviousAltHost()) { EnterAltHost(altHost); } } } } if (currentAltHost) { return; } if(focusedComposition && focusedComposition->HasEventReceiver()) { OnKeyInput(info, focusedComposition, &GuiGraphicsEventReceiver::systemKeyDown); } } void GuiGraphicsHost::SysKeyUp(const NativeWindowKeyInfo& info) { if (!info.ctrl && !info.shift && info.code == VKEY_MENU && hostRecord.nativeWindow) { if (hostRecord.nativeWindow) { hostRecord.nativeWindow->SupressAlt(); } } if (focusedComposition && focusedComposition->HasEventReceiver()) { OnKeyInput(info, focusedComposition, &GuiGraphicsEventReceiver::systemKeyUp); } } void GuiGraphicsHost::Char(const NativeWindowCharInfo& info) { if (!currentAltHost && !supressAltKey) { if(focusedComposition && focusedComposition->HasEventReceiver()) { OnCharInput(info, focusedComposition, &GuiGraphicsEventReceiver::charInput); } } } void GuiGraphicsHost::GlobalTimer() { timerManager.Play(); DateTime now=DateTime::UtcTime(); if(now.totalMilliseconds-lastCaretTime>=CaretInterval) { lastCaretTime=now.totalMilliseconds; if(focusedComposition && focusedComposition->HasEventReceiver()) { focusedComposition->GetEventReceiver()->caretNotify.Execute(GuiEventArgs(focusedComposition)); } } Render(false); } GuiGraphicsHost::GuiGraphicsHost(controls::GuiControlHost* _controlHost, GuiGraphicsComposition* boundsComposition) :controlHost(_controlHost) { hostRecord.host = this; windowComposition=new GuiWindowComposition; windowComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); windowComposition->AddChild(boundsComposition); RefreshRelatedHostRecord(nullptr); } GuiGraphicsHost::~GuiGraphicsHost() { windowComposition->RemoveChild(windowComposition->Children()[0]); NotifyFinalizeInstance(windowComposition); if(shortcutKeyManager) { delete shortcutKeyManager; shortcutKeyManager=0; } delete windowComposition; } INativeWindow* GuiGraphicsHost::GetNativeWindow() { return hostRecord.nativeWindow; } void GuiGraphicsHost::SetNativeWindow(INativeWindow* _nativeWindow) { if (hostRecord.nativeWindow != _nativeWindow) { if (hostRecord.nativeWindow) { GetCurrentController()->CallbackService()->UninstallListener(this); hostRecord.nativeWindow->UninstallListener(this); } if (_nativeWindow) { _nativeWindow->InstallListener(this); GetCurrentController()->CallbackService()->InstallListener(this); previousClientSize = _nativeWindow->GetClientSize(); minSize = windowComposition->GetPreferredBounds().GetSize(); _nativeWindow->SetCaretPoint(caretPoint); needRender = true; } RefreshRelatedHostRecord(_nativeWindow); } } GuiGraphicsComposition* GuiGraphicsHost::GetMainComposition() { return windowComposition; } void GuiGraphicsHost::Render(bool forceUpdate) { if (!forceUpdate && !needRender) { return; } needRender = false; if(hostRecord.nativeWindow && hostRecord.nativeWindow->IsVisible()) { supressPaint = true; hostRecord.renderTarget->StartRendering(); windowComposition->Render(Size()); { auto bounds = windowComposition->GetBounds(); auto preferred = windowComposition->GetPreferredBounds(); auto width = bounds.Width() > preferred.Width() ? bounds.Width() : preferred.Width(); auto height = bounds.Height() > preferred.Height() ? bounds.Height() : preferred.Height(); if (width != bounds.Width() || height != bounds.Height()) { controlHost->UpdateClientSizeAfterRendering(Size(width, height)); } } auto result = hostRecord.renderTarget->StopRendering(); hostRecord.nativeWindow->RedrawContent(); supressPaint = false; switch (result) { case RenderTargetFailure::ResizeWhileRendering: { GetGuiGraphicsResourceManager()->ResizeRenderTarget(hostRecord.nativeWindow); needRender = true; } break; case RenderTargetFailure::LostDevice: { windowComposition->UpdateRelatedHostRecord(nullptr); GetGuiGraphicsResourceManager()->RecreateRenderTarget(hostRecord.nativeWindow); RefreshRelatedHostRecord(hostRecord.nativeWindow); needRender = true; } break; default:; } } } void GuiGraphicsHost::RequestRender() { needRender = true; } IGuiShortcutKeyManager* GuiGraphicsHost::GetShortcutKeyManager() { return shortcutKeyManager; } void GuiGraphicsHost::SetShortcutKeyManager(IGuiShortcutKeyManager* value) { shortcutKeyManager=value; } bool GuiGraphicsHost::SetFocus(GuiGraphicsComposition* composition) { if(!composition || composition->GetRelatedGraphicsHost()!=this) { return false; } if(focusedComposition && focusedComposition->HasEventReceiver()) { GuiEventArgs arguments; arguments.compositionSource=focusedComposition; arguments.eventSource=focusedComposition; focusedComposition->GetEventReceiver()->lostFocus.Execute(arguments); } focusedComposition=composition; SetCaretPoint(Point(0, 0)); if(focusedComposition && focusedComposition->HasEventReceiver()) { GuiEventArgs arguments; arguments.compositionSource=focusedComposition; arguments.eventSource=focusedComposition; focusedComposition->GetEventReceiver()->gotFocus.Execute(arguments); } return true; } GuiGraphicsComposition* GuiGraphicsHost::GetFocusedComposition() { return focusedComposition; } Point GuiGraphicsHost::GetCaretPoint() { return caretPoint; } void GuiGraphicsHost::SetCaretPoint(Point value, GuiGraphicsComposition* referenceComposition) { if (referenceComposition) { Rect bounds = referenceComposition->GetGlobalBounds(); value.x += bounds.x1; value.y += bounds.y1; } caretPoint = value; if (hostRecord.nativeWindow) { hostRecord.nativeWindow->SetCaretPoint(caretPoint); } } GuiGraphicsTimerManager* GuiGraphicsHost::GetTimerManager() { return &timerManager; } void GuiGraphicsHost::DisconnectComposition(GuiGraphicsComposition* composition) { DisconnectCompositionInternal(composition); } /*********************************************************************** GuiShortcutKeyItem ***********************************************************************/ GuiShortcutKeyItem::GuiShortcutKeyItem(GuiShortcutKeyManager* _shortcutKeyManager, bool _ctrl, bool _shift, bool _alt, vint _key) :shortcutKeyManager(_shortcutKeyManager) ,ctrl(_ctrl) ,shift(_shift) ,alt(_alt) ,key(_key) { } GuiShortcutKeyItem::~GuiShortcutKeyItem() { } IGuiShortcutKeyManager* GuiShortcutKeyItem::GetManager() { return shortcutKeyManager; } WString GuiShortcutKeyItem::GetName() { WString name; if(ctrl) name+=L"Ctrl+"; if(shift) name+=L"Shift+"; if(alt) name+=L"Alt+"; name+=GetCurrentController()->InputService()->GetKeyName(key); return name; } bool GuiShortcutKeyItem::CanActivate(const NativeWindowKeyInfo& info) { return info.ctrl==ctrl && info.shift==shift && info.alt==alt && info.code==key; } bool GuiShortcutKeyItem::CanActivate(bool _ctrl, bool _shift, bool _alt, vint _key) { return _ctrl==ctrl && _shift==shift && _alt==alt && _key==key; } /*********************************************************************** GuiShortcutKeyManager ***********************************************************************/ GuiShortcutKeyManager::GuiShortcutKeyManager() { } GuiShortcutKeyManager::~GuiShortcutKeyManager() { } vint GuiShortcutKeyManager::GetItemCount() { return shortcutKeyItems.Count(); } IGuiShortcutKeyItem* GuiShortcutKeyManager::GetItem(vint index) { return shortcutKeyItems[index].Obj(); } bool GuiShortcutKeyManager::Execute(const NativeWindowKeyInfo& info) { bool executed=false; FOREACH(Ptr, item, shortcutKeyItems) { if(item->CanActivate(info)) { GuiEventArgs arguments; item->Executed.Execute(arguments); executed=true; } } return executed; } IGuiShortcutKeyItem* GuiShortcutKeyManager::CreateShortcut(bool ctrl, bool shift, bool alt, vint key) { FOREACH(Ptr, item, shortcutKeyItems) { if(item->CanActivate(ctrl, shift, alt, key)) { return item.Obj(); } } Ptr item=new GuiShortcutKeyItem(this, ctrl, shift, alt, key); shortcutKeyItems.Add(item); return item.Obj(); } bool GuiShortcutKeyManager::DestroyShortcut(bool ctrl, bool shift, bool alt, vint key) { FOREACH(Ptr, item, shortcutKeyItems) { if(item->CanActivate(ctrl, shift, alt, key)) { shortcutKeyItems.Remove(item.Obj()); return true; } } return false; } IGuiShortcutKeyItem* GuiShortcutKeyManager::TryGetShortcut(bool ctrl, bool shift, bool alt, vint key) { FOREACH(Ptr, item, shortcutKeyItems) { if(item->CanActivate(ctrl, shift, alt, key)) { return item.Obj(); } } return 0; } } } } /*********************************************************************** .\CONTROLS\GUIBUTTONCONTROLS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace elements; using namespace compositions; using namespace collections; using namespace reflection::description; /*********************************************************************** GuiButton ***********************************************************************/ void GuiButton::BeforeControlTemplateUninstalled_() { } void GuiButton::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); GetControlTemplateObject(true)->SetState(controlState); } void GuiButton::OnParentLineChanged() { GuiControl::OnParentLineChanged(); if(GetRelatedControlHost()==0) { mousePressing=false; mouseHoving=false; UpdateControlState(); } } void GuiButton::OnActiveAlt() { Clicked.Execute(GetNotifyEventArguments()); } void GuiButton::UpdateControlState() { auto newControlState = ButtonState::Normal; if (mousePressing) { if (mouseHoving) { newControlState = ButtonState::Pressed; } else { newControlState = ButtonState::Active; } } else { if (mouseHoving) { newControlState = ButtonState::Active; } else { newControlState = ButtonState::Normal; } } if (controlState != newControlState) { controlState = newControlState; GetControlTemplateObject(true)->SetState(controlState); } } void GuiButton::OnLeftButtonDown(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { if(arguments.eventSource==boundsComposition) { mousePressing=true; boundsComposition->GetRelatedGraphicsHost()->SetFocus(boundsComposition); UpdateControlState(); if(!clickOnMouseUp && arguments.eventSource->GetAssociatedControl()==this) { Clicked.Execute(GetNotifyEventArguments()); } } } void GuiButton::OnLeftButtonUp(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { if(arguments.eventSource==boundsComposition) { mousePressing=false; UpdateControlState(); } if(GetVisuallyEnabled()) { if(mouseHoving && clickOnMouseUp) { auto eventSource = arguments.eventSource->GetAssociatedControl(); while (eventSource && eventSource != this) { if (eventSource->GetFocusableComposition()) { return; } eventSource = eventSource->GetParent(); } Clicked.Execute(GetNotifyEventArguments()); } } } void GuiButton::OnMouseEnter(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if(arguments.eventSource==boundsComposition) { mouseHoving=true; UpdateControlState(); } } void GuiButton::OnMouseLeave(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if(arguments.eventSource==boundsComposition) { mouseHoving=false; UpdateControlState(); } } GuiButton::GuiButton(theme::ThemeName themeName) :GuiControl(themeName) { Clicked.SetAssociatedComposition(boundsComposition); SetFocusableComposition(boundsComposition); boundsComposition->GetEventReceiver()->leftButtonDown.AttachMethod(this, &GuiButton::OnLeftButtonDown); boundsComposition->GetEventReceiver()->leftButtonUp.AttachMethod(this, &GuiButton::OnLeftButtonUp); boundsComposition->GetEventReceiver()->mouseEnter.AttachMethod(this, &GuiButton::OnMouseEnter); boundsComposition->GetEventReceiver()->mouseLeave.AttachMethod(this, &GuiButton::OnMouseLeave); } GuiButton::~GuiButton() { } bool GuiButton::GetClickOnMouseUp() { return clickOnMouseUp; } void GuiButton::SetClickOnMouseUp(bool value) { clickOnMouseUp=value; } /*********************************************************************** GuiSelectableButton::GroupController ***********************************************************************/ GuiSelectableButton::GroupController::GroupController() { } GuiSelectableButton::GroupController::~GroupController() { for(vint i=buttons.Count()-1;i>=0;i--) { buttons[i]->SetGroupController(0); } } void GuiSelectableButton::GroupController::Attach(GuiSelectableButton* button) { if(!buttons.Contains(button)) { buttons.Add(button); } } void GuiSelectableButton::GroupController::Detach(GuiSelectableButton* button) { buttons.Remove(button); } /*********************************************************************** GuiSelectableButton::MutexGroupController ***********************************************************************/ GuiSelectableButton::MutexGroupController::MutexGroupController() :suppress(false) { } GuiSelectableButton::MutexGroupController::~MutexGroupController() { } void GuiSelectableButton::MutexGroupController::OnSelectedChanged(GuiSelectableButton* button) { if(!suppress) { suppress=true; for(vint i=0;iSetSelected(buttons[i]==button); } suppress=false; } } /*********************************************************************** GuiSelectableButton ***********************************************************************/ void GuiSelectableButton::BeforeControlTemplateUninstalled_() { } void GuiSelectableButton::AfterControlTemplateInstalled_(bool initialize) { GetControlTemplateObject(true)->SetSelected(isSelected); } void GuiSelectableButton::OnClicked(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if(autoSelection) { SetSelected(!GetSelected()); } } GuiSelectableButton::GuiSelectableButton(theme::ThemeName themeName) :GuiButton(themeName) { GroupControllerChanged.SetAssociatedComposition(boundsComposition); AutoSelectionChanged.SetAssociatedComposition(boundsComposition); SelectedChanged.SetAssociatedComposition(boundsComposition); Clicked.AttachMethod(this, &GuiSelectableButton::OnClicked); } GuiSelectableButton::~GuiSelectableButton() { if(groupController) { groupController->Detach(this); } } GuiSelectableButton::GroupController* GuiSelectableButton::GetGroupController() { return groupController; } void GuiSelectableButton::SetGroupController(GroupController* value) { if(groupController) { groupController->Detach(this); } groupController=value; if(groupController) { groupController->Attach(this); } GroupControllerChanged.Execute(GetNotifyEventArguments()); } bool GuiSelectableButton::GetAutoSelection() { return autoSelection; } void GuiSelectableButton::SetAutoSelection(bool value) { if(autoSelection!=value) { autoSelection=value; AutoSelectionChanged.Execute(GetNotifyEventArguments()); } } bool GuiSelectableButton::GetSelected() { return isSelected; } void GuiSelectableButton::SetSelected(bool value) { if (isSelected != value) { isSelected = value; GetControlTemplateObject(true)->SetSelected(isSelected); if (groupController) { groupController->OnSelectedChanged(this); } SelectedChanged.Execute(GetNotifyEventArguments()); } } } } } /*********************************************************************** .\CONTROLS\GUICONTAINERCONTROLS.CPP ***********************************************************************/ namespace vl { namespace presentation { using namespace compositions; namespace controls { /*********************************************************************** GuiTabPage ***********************************************************************/ bool GuiTabPage::IsAltAvailable() { return false; } GuiTabPage::GuiTabPage(theme::ThemeName themeName) :GuiCustomControl(themeName) { } GuiTabPage::~GuiTabPage() { FinalizeAggregation(); } GuiTab* GuiTabPage::GetOwnerTab() { return tab; } /*********************************************************************** GuiTabPageList ***********************************************************************/ bool GuiTabPageList::QueryInsert(vint index, GuiTabPage* const& value) { return !items.Contains(value) && value->tab == nullptr; } void GuiTabPageList::AfterInsert(vint index, GuiTabPage* const& value) { value->tab = tab; value->SetVisible(false); value->boundsComposition->SetAlignmentToParent(Margin(0, 0, 0, 0)); tab->containerComposition->AddChild(value->boundsComposition); if (!tab->selectedPage) { tab->SetSelectedPage(value); } } void GuiTabPageList::BeforeRemove(vint index, GuiTabPage* const& value) { tab->containerComposition->RemoveChild(value->boundsComposition); value->tab = nullptr; if (items.Count() == 0) { tab->SetSelectedPage(nullptr); } else if (tab->selectedPage == value) { tab->SetSelectedPage(items[0]); } } GuiTabPageList::GuiTabPageList(GuiTab* _tab) :tab(_tab) { } GuiTabPageList::~GuiTabPageList() { } /*********************************************************************** GuiTab ***********************************************************************/ void GuiTab::BeforeControlTemplateUninstalled_() { auto ct = GetControlTemplateObject(false); if (!ct) return; ct->SetCommands(nullptr); ct->SetTabPages(nullptr); ct->SetSelectedTabPage(nullptr); } void GuiTab::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); ct->SetCommands(commandExecutor.Obj()); ct->SetTabPages(tabPages.GetWrapper()); ct->SetSelectedTabPage(selectedPage); } GuiTab::CommandExecutor::CommandExecutor(GuiTab* _tab) :tab(_tab) { } GuiTab::CommandExecutor::~CommandExecutor() { } void GuiTab::CommandExecutor::ShowTab(vint index) { tab->SetSelectedPage(tab->GetPages().Get(index)); } GuiTab::GuiTab(theme::ThemeName themeName) :GuiControl(themeName) , tabPages(this) { commandExecutor = new CommandExecutor(this); } GuiTab::~GuiTab() { } collections::ObservableList& GuiTab::GetPages() { return tabPages; } GuiTabPage* GuiTab::GetSelectedPage() { return selectedPage; } bool GuiTab::SetSelectedPage(GuiTabPage* value) { if (!value) { if (tabPages.Count() == 0) { selectedPage = nullptr; } } else if (value->GetOwnerTab() == this) { if (selectedPage == value) { return true; } selectedPage = value; FOREACH(GuiTabPage*, tabPage, tabPages) { tabPage->SetVisible(tabPage == selectedPage); } } if (auto ct = GetControlTemplateObject(false)) { ct->SetSelectedTabPage(selectedPage); } SelectedPageChanged.Execute(GetNotifyEventArguments()); return selectedPage == value; } /*********************************************************************** GuiScrollView ***********************************************************************/ void GuiScrollView::BeforeControlTemplateUninstalled_() { auto ct = GetControlTemplateObject(false); if (!ct) return; if (auto scroll = ct->GetHorizontalScroll()) { scroll->PositionChanged.Detach(hScrollHandler); } if (auto scroll = ct->GetVerticalScroll()) { scroll->PositionChanged.Detach(vScrollHandler); } ct->GetEventReceiver()->horizontalWheel.Detach(hWheelHandler); ct->GetEventReceiver()->verticalWheel.Detach(vWheelHandler); ct->BoundsChanged.Detach(containerBoundsChangedHandler); hScrollHandler = nullptr; vScrollHandler = nullptr; hWheelHandler = nullptr; vWheelHandler = nullptr; containerBoundsChangedHandler = nullptr; supressScrolling = false; } void GuiScrollView::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); if (auto scroll = ct->GetHorizontalScroll()) { hScrollHandler = scroll->PositionChanged.AttachMethod(this, &GuiScrollView::OnHorizontalScroll); } if (auto scroll = ct->GetVerticalScroll()) { vScrollHandler = scroll->PositionChanged.AttachMethod(this, &GuiScrollView::OnVerticalScroll); } hWheelHandler = ct->GetEventReceiver()->horizontalWheel.AttachMethod(this, &GuiScrollView::OnHorizontalWheel); vWheelHandler = ct->GetEventReceiver()->verticalWheel.AttachMethod(this, &GuiScrollView::OnVerticalWheel); containerBoundsChangedHandler = ct->BoundsChanged.AttachMethod(this, &GuiScrollView::OnContainerBoundsChanged); CalculateView(); } void GuiScrollView::OnContainerBoundsChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { CalculateView(); } void GuiScrollView::OnHorizontalScroll(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if(!supressScrolling) { CallUpdateView(); } } void GuiScrollView::OnVerticalScroll(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if(!supressScrolling) { CallUpdateView(); } } void GuiScrollView::OnHorizontalWheel(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { if(!supressScrolling) { if (auto scroll = GetControlTemplateObject(true)->GetHorizontalScroll()) { vint position = scroll->GetPosition(); vint move = scroll->GetSmallMove(); position -= move * arguments.wheel / 60; scroll->SetPosition(position); } } } void GuiScrollView::OnVerticalWheel(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { if(!supressScrolling && GetVisuallyEnabled()) { if (auto scroll = GetControlTemplateObject(true)->GetVerticalScroll()) { vint position = scroll->GetPosition(); vint move = scroll->GetSmallMove(); position -= move * arguments.wheel / 60; scroll->SetPosition(position); } } } void GuiScrollView::CallUpdateView() { Rect viewBounds=GetViewBounds(); UpdateView(viewBounds); } bool GuiScrollView::AdjustView(Size fullSize) { auto ct = GetControlTemplateObject(true); auto hScroll = ct->GetHorizontalScroll(); auto vScroll = ct->GetVerticalScroll(); Size viewSize = ct->GetContainerComposition()->GetBounds().GetSize(); auto hVisible = hScroll ? hScroll->GetVisible() : false; auto vVisible = vScroll ? vScroll->GetVisible() : false; if (hScroll) { if (fullSize.x <= viewSize.x) { hScroll->SetVisible(horizontalAlwaysVisible); hScroll->SetEnabled(false); hScroll->SetPosition(0); } else { hScroll->SetVisible(true); hScroll->SetEnabled(true); hScroll->SetTotalSize(fullSize.x); hScroll->SetPageSize(viewSize.x); } } if (vScroll) { if (fullSize.y <= viewSize.y) { vScroll->SetVisible(verticalAlwaysVisible); vScroll->SetEnabled(false); vScroll->SetPosition(0); } else { vScroll->SetVisible(true); vScroll->SetEnabled(true); vScroll->SetTotalSize(fullSize.y); vScroll->SetPageSize(viewSize.y); } } auto hVisible2 = hScroll ? hScroll->GetVisible() : false; auto vVisible2 = vScroll ? vScroll->GetVisible() : false; return hVisible != hVisible2 || vVisible != vVisible2; } GuiScrollView::GuiScrollView(theme::ThemeName themeName) :GuiControl(themeName) { containerComposition->BoundsChanged.AttachMethod(this, &GuiScrollView::OnContainerBoundsChanged); } vint GuiScrollView::GetSmallMove() { return GetFont().size * 2; } Size GuiScrollView::GetBigMove() { return GetViewSize(); } GuiScrollView::~GuiScrollView() { } void GuiScrollView::SetFont(const FontProperties& value) { GuiControl::SetFont(value); CalculateView(); } void GuiScrollView::CalculateView() { auto ct = GetControlTemplateObject(true); if (!supressScrolling) { Size fullSize = QueryFullSize(); while (true) { bool flagA = AdjustView(fullSize); bool flagB = AdjustView(fullSize); supressScrolling = true; CallUpdateView(); supressScrolling = false; Size newSize = QueryFullSize(); if (fullSize == newSize) { vint smallMove = GetSmallMove(); Size bigMove = GetBigMove(); if (auto scroll = ct->GetHorizontalScroll()) { scroll->SetSmallMove(smallMove); scroll->SetBigMove(bigMove.x); } if (auto scroll = ct->GetVerticalScroll()) { scroll->SetSmallMove(smallMove); scroll->SetBigMove(bigMove.y); } if (!flagA && !flagB) { break; } } else { fullSize = newSize; } } } } Size GuiScrollView::GetViewSize() { Size viewSize = GetControlTemplateObject(true)->GetContainerComposition()->GetBounds().GetSize(); return viewSize; } Rect GuiScrollView::GetViewBounds() { return Rect(GetViewPosition(), GetViewSize()); } Point GuiScrollView::GetViewPosition() { auto ct = GetControlTemplateObject(true); auto hScroll = ct->GetHorizontalScroll(); auto vScroll = ct->GetVerticalScroll(); return Point(hScroll ? hScroll->GetPosition() : 0, vScroll ? vScroll->GetPosition() : 0); } void GuiScrollView::SetViewPosition(Point value) { auto ct = GetControlTemplateObject(true); if (auto hScroll = ct->GetHorizontalScroll()) { hScroll->SetPosition(value.x); } if (auto vScroll = ct->GetVerticalScroll()) { vScroll->SetPosition(value.y); } } GuiScroll* GuiScrollView::GetHorizontalScroll() { return GetControlTemplateObject(true)->GetHorizontalScroll(); } GuiScroll* GuiScrollView::GetVerticalScroll() { return GetControlTemplateObject(true)->GetVerticalScroll(); } bool GuiScrollView::GetHorizontalAlwaysVisible() { return horizontalAlwaysVisible; } void GuiScrollView::SetHorizontalAlwaysVisible(bool value) { if (horizontalAlwaysVisible != value) { horizontalAlwaysVisible = value; CalculateView(); } } bool GuiScrollView::GetVerticalAlwaysVisible() { return verticalAlwaysVisible; } void GuiScrollView::SetVerticalAlwaysVisible(bool value) { if (verticalAlwaysVisible != value) { verticalAlwaysVisible = value; CalculateView(); } } /*********************************************************************** GuiScrollContainer ***********************************************************************/ Size GuiScrollContainer::QueryFullSize() { return containerComposition->GetBounds().GetSize(); } void GuiScrollContainer::UpdateView(Rect viewBounds) { auto leftTop = Point(-viewBounds.x1, -viewBounds.y1); containerComposition->SetBounds(Rect(leftTop, Size(0, 0))); } GuiScrollContainer::GuiScrollContainer(theme::ThemeName themeName) :GuiScrollView(themeName) { containerComposition->SetAlignmentToParent(Margin(-1, -1, -1, -1)); UpdateView(Rect(0, 0, 0, 0)); } GuiScrollContainer::~GuiScrollContainer() { } bool GuiScrollContainer::GetExtendToFullWidth() { return extendToFullWidth; } void GuiScrollContainer::SetExtendToFullWidth(bool value) { if (extendToFullWidth != value) { extendToFullWidth = value; auto margin = containerComposition->GetAlignmentToParent(); if (value) { containerComposition->SetAlignmentToParent(Margin(0, margin.top, 0, margin.bottom)); } else { containerComposition->SetAlignmentToParent(Margin(-1, margin.top, -1, margin.bottom)); } } } bool GuiScrollContainer::GetExtendToFullHeight() { return extendToFullHeight; } void GuiScrollContainer::SetExtendToFullHeight(bool value) { if (extendToFullHeight != value) { extendToFullHeight = value; auto margin = containerComposition->GetAlignmentToParent(); if (value) { containerComposition->SetAlignmentToParent(Margin(margin.left, 0, margin.right, 0)); } else { containerComposition->SetAlignmentToParent(Margin(margin.left, -1, margin.right, -1)); } } } } } } /*********************************************************************** .\CONTROLS\GUISCROLLCONTROLS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace elements; using namespace compositions; using namespace collections; using namespace reflection::description; /*********************************************************************** GuiScroll::CommandExecutor ***********************************************************************/ GuiScroll::CommandExecutor::CommandExecutor(GuiScroll* _scroll) :scroll(_scroll) { } GuiScroll::CommandExecutor::~CommandExecutor() { } void GuiScroll::CommandExecutor::SmallDecrease() { scroll->SetPosition(scroll->GetPosition()-scroll->GetSmallMove()); } void GuiScroll::CommandExecutor::SmallIncrease() { scroll->SetPosition(scroll->GetPosition()+scroll->GetSmallMove()); } void GuiScroll::CommandExecutor::BigDecrease() { scroll->SetPosition(scroll->GetPosition()-scroll->GetBigMove()); } void GuiScroll::CommandExecutor::BigIncrease() { scroll->SetPosition(scroll->GetPosition()+scroll->GetBigMove()); } void GuiScroll::CommandExecutor::SetTotalSize(vint value) { scroll->SetTotalSize(value); } void GuiScroll::CommandExecutor::SetPageSize(vint value) { scroll->SetPageSize(value); } void GuiScroll::CommandExecutor::SetPosition(vint value) { scroll->SetPosition(value); } /*********************************************************************** GuiScroll ***********************************************************************/ void GuiScroll::BeforeControlTemplateUninstalled_() { auto ct = GetControlTemplateObject(false); if (!ct) return; ct->SetCommands(nullptr); } void GuiScroll::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); ct->SetCommands(commandExecutor.Obj()); ct->SetPageSize(pageSize); ct->SetTotalSize(totalSize); ct->SetPosition(position); } GuiScroll::GuiScroll(theme::ThemeName themeName) :GuiControl(themeName) { TotalSizeChanged.SetAssociatedComposition(boundsComposition); PageSizeChanged.SetAssociatedComposition(boundsComposition); PositionChanged.SetAssociatedComposition(boundsComposition); SmallMoveChanged.SetAssociatedComposition(boundsComposition); BigMoveChanged.SetAssociatedComposition(boundsComposition); commandExecutor = new CommandExecutor(this); } GuiScroll::~GuiScroll() { } vint GuiScroll::GetTotalSize() { return totalSize; } void GuiScroll::SetTotalSize(vint value) { if(totalSize!=value && 0totalSize) { SetPageSize(totalSize); } if(position>GetMaxPosition()) { SetPosition(GetMaxPosition()); } GetControlTemplateObject(true)->SetTotalSize(totalSize); TotalSizeChanged.Execute(GetNotifyEventArguments()); } } vint GuiScroll::GetPageSize() { return pageSize; } void GuiScroll::SetPageSize(vint value) { if(pageSize!=value && 0<=value && value<=totalSize) { pageSize=value; if(position>GetMaxPosition()) { SetPosition(GetMaxPosition()); } GetControlTemplateObject(true)->SetPageSize(pageSize); PageSizeChanged.Execute(GetNotifyEventArguments()); } } vint GuiScroll::GetPosition() { return position; } void GuiScroll::SetPosition(vint value) { vint min=GetMinPosition(); vint max=GetMaxPosition(); vint newPosition= valuemax?max: value; if(position!=newPosition) { position=newPosition; GetControlTemplateObject(true)->SetPosition(position); PositionChanged.Execute(GetNotifyEventArguments()); } } vint GuiScroll::GetSmallMove() { return smallMove; } void GuiScroll::SetSmallMove(vint value) { if(value>0 && smallMove!=value) { smallMove=value; SmallMoveChanged.Execute(GetNotifyEventArguments()); } } vint GuiScroll::GetBigMove() { return bigMove; } void GuiScroll::SetBigMove(vint value) { if(value>0 && bigMove!=value) { bigMove=value; BigMoveChanged.Execute(GetNotifyEventArguments()); } } vint GuiScroll::GetMinPosition() { return 0; } vint GuiScroll::GetMaxPosition() { return totalSize-pageSize; } } } } /*********************************************************************** .\CONTROLS\LISTCONTROLPACKAGE\GUILISTCONTROLS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace collections; using namespace elements; using namespace compositions; /*********************************************************************** GuiListControl::ItemCallback ***********************************************************************/ Ptr GuiListControl::ItemCallback::InstallStyle(ItemStyle* style, vint itemIndex, compositions::GuiBoundsComposition* itemComposition) { auto handler = style->BoundsChanged.AttachMethod(this, &ItemCallback::OnStyleBoundsChanged); listControl->GetContainerComposition()->AddChild(itemComposition ? itemComposition : style); listControl->OnStyleInstalled(itemIndex, style); return handler; } GuiListControl::ItemStyle* GuiListControl::ItemCallback::UninstallStyle(vint index) { auto style = installedStyles.Keys()[index]; auto handler = installedStyles.Values()[index]; listControl->OnStyleUninstalled(style); listControl->GetContainerComposition()->RemoveChild(style); style->BoundsChanged.Detach(handler); return style; } void GuiListControl::ItemCallback::OnStyleBoundsChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { listControl->CalculateView(); } GuiListControl::ItemCallback::ItemCallback(GuiListControl* _listControl) :listControl(_listControl) { } GuiListControl::ItemCallback::~ItemCallback() { ClearCache(); } void GuiListControl::ItemCallback::ClearCache() { for (vint i = 0; i < installedStyles.Count(); i++) { auto style = UninstallStyle(i); SafeDeleteComposition(style); } installedStyles.Clear(); } void GuiListControl::ItemCallback::OnAttached(IItemProvider* provider) { itemProvider = provider; } void GuiListControl::ItemCallback::OnItemModified(vint start, vint count, vint newCount) { listControl->OnItemModified(start, count, newCount); } GuiListControl::ItemStyle* GuiListControl::ItemCallback::RequestItem(vint itemIndex, compositions::GuiBoundsComposition* itemComposition) { CHECK_ERROR(0 <= itemIndex && itemIndex < itemProvider->Count(), L"GuiListControl::ItemCallback::RequestItem(vint)#Index out of range."); CHECK_ERROR(listControl->itemStyleProperty, L"GuiListControl::ItemCallback::RequestItem(vint)#SetItemTemplate function should be called before adding items to the list control."); auto style = listControl->itemStyleProperty(itemProvider->GetBindingValue(itemIndex)); auto handler = InstallStyle(style, itemIndex, itemComposition); installedStyles.Add(style, handler); return style; } void GuiListControl::ItemCallback::ReleaseItem(ItemStyle* style) { vint index = installedStyles.Keys().IndexOf(style); if (index != -1) { auto style = UninstallStyle(index); installedStyles.Remove(style); SafeDeleteComposition(style); } } void GuiListControl::ItemCallback::SetViewLocation(Point value) { Rect virtualRect(value, listControl->GetViewSize()); Rect realRect = listControl->axis->VirtualRectToRealRect(listControl->fullSize, virtualRect); listControl->SetViewPosition(realRect.LeftTop()); } Size GuiListControl::ItemCallback::GetStylePreferredSize(compositions::GuiBoundsComposition* style) { Size size = style->GetPreferredBounds().GetSize(); return listControl->axis->RealSizeToVirtualSize(size); } void GuiListControl::ItemCallback::SetStyleAlignmentToParent(compositions::GuiBoundsComposition* style, Margin margin) { Margin newMargin = listControl->axis->VirtualMarginToRealMargin(margin); style->SetAlignmentToParent(newMargin); } Rect GuiListControl::ItemCallback::GetStyleBounds(compositions::GuiBoundsComposition* style) { Rect bounds = style->GetBounds(); return listControl->axis->RealRectToVirtualRect(listControl->GetViewSize(), bounds); } void GuiListControl::ItemCallback::SetStyleBounds(compositions::GuiBoundsComposition* style, Rect bounds) { Rect newBounds = listControl->axis->VirtualRectToRealRect(listControl->GetViewSize(), bounds); return style->SetBounds(newBounds); } compositions::GuiGraphicsComposition* GuiListControl::ItemCallback::GetContainerComposition() { return listControl->GetContainerComposition(); } void GuiListControl::ItemCallback::OnTotalSizeChanged() { listControl->CalculateView(); } /*********************************************************************** GuiListControl ***********************************************************************/ void GuiListControl::BeforeControlTemplateUninstalled_() { } void GuiListControl::AfterControlTemplateInstalled_(bool initialize) { if (itemArranger) { itemArranger->ReloadVisibleStyles(); CalculateView(); } } void GuiListControl::OnItemModified(vint start, vint count, vint newCount) { } void GuiListControl::OnStyleInstalled(vint itemIndex, ItemStyle* style) { style->SetFont(GetFont()); style->SetContext(GetContext()); style->SetText(itemProvider->GetTextValue(itemIndex)); style->SetVisuallyEnabled(GetVisuallyEnabled()); style->SetSelected(false); style->SetIndex(itemIndex); style->Initialize(this); AttachItemEvents(style); } void GuiListControl::OnStyleUninstalled(ItemStyle* style) { DetachItemEvents(style); } void GuiListControl::OnRenderTargetChanged(elements::IGuiGraphicsRenderTarget* renderTarget) { SetStyleAndArranger(itemStyleProperty, itemArranger); GuiScrollView::OnRenderTargetChanged(renderTarget); } void GuiListControl::OnBeforeReleaseGraphicsHost() { GuiScrollView::OnBeforeReleaseGraphicsHost(); SetStyleAndArranger({}, nullptr); } Size GuiListControl::QueryFullSize() { Size virtualSize = itemArranger ? itemArranger->GetTotalSize() : Size(0, 0); fullSize = axis->VirtualSizeToRealSize(virtualSize); return fullSize; } void GuiListControl::UpdateView(Rect viewBounds) { if (itemArranger) { Rect newBounds = axis->RealRectToVirtualRect(fullSize, viewBounds); itemArranger->OnViewChanged(newBounds); } } void GuiListControl::OnBoundsMouseButtonDown(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { if(GetVisuallyEnabled()) { boundsComposition->GetRelatedGraphicsHost()->SetFocus(boundsComposition); } } void GuiListControl::SetStyleAndArranger(ItemStyleProperty styleProperty, Ptr arranger) { if (itemArranger) { itemArranger->DetachListControl(); itemArranger->SetCallback(nullptr); itemProvider->DetachCallback(itemArranger.Obj()); } callback->ClearCache(); itemStyleProperty = styleProperty; itemArranger = arranger; if (auto scroll = GetVerticalScroll()) { scroll->SetPosition(0); } if (auto scroll = GetHorizontalScroll()) { scroll->SetPosition(0); } if (itemArranger) { itemProvider->AttachCallback(itemArranger.Obj()); itemArranger->SetCallback(callback.Obj()); itemArranger->AttachListControl(this); } CalculateView(); } void GuiListControl::OnClientBoundsChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { auto args = GetNotifyEventArguments(); AdoptedSizeInvalidated.Execute(args); } void GuiListControl::OnVisuallyEnabledChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { FOREACH(ItemStyle*, style, visibleStyles.Keys()) { style->SetVisuallyEnabled(GetVisuallyEnabled()); } } void GuiListControl::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { FOREACH(ItemStyle*, style, visibleStyles.Keys()) { style->SetFont(GetFont()); } } void GuiListControl::OnContextChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { FOREACH(ItemStyle*, style, visibleStyles.Keys()) { style->SetContext(GetContext()); } } void GuiListControl::OnItemMouseEvent(compositions::GuiItemMouseEvent& itemEvent, ItemStyle* style, compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { if (itemArranger && GetVisuallyEnabled()) { vint itemIndex = itemArranger->GetVisibleIndex(style); if (itemIndex != -1) { GuiItemMouseEventArgs redirectArguments; (GuiMouseEventArgs&)redirectArguments = arguments; redirectArguments.compositionSource = boundsComposition; redirectArguments.eventSource = boundsComposition; redirectArguments.itemIndex = itemIndex; itemEvent.Execute(redirectArguments); arguments = redirectArguments; } } } void GuiListControl::OnItemNotifyEvent(compositions::GuiItemNotifyEvent& itemEvent, ItemStyle* style, compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if (itemArranger && GetVisuallyEnabled()) { vint itemIndex = itemArranger->GetVisibleIndex(style); if (itemIndex != -1) { GuiItemEventArgs redirectArguments; (GuiEventArgs&)redirectArguments = arguments; redirectArguments.compositionSource = boundsComposition; redirectArguments.eventSource = boundsComposition; redirectArguments.itemIndex = itemIndex; itemEvent.Execute(redirectArguments); arguments = redirectArguments; } } } #define ATTACH_ITEM_MOUSE_EVENT(EVENTNAME, ITEMEVENTNAME)\ {\ Func func(this, &GuiListControl::OnItemMouseEvent);\ helper->EVENTNAME##Handler = style->GetEventReceiver()->EVENTNAME.AttachFunction(\ Curry(Curry(func)(ITEMEVENTNAME))(style)\ );\ }\ #define ATTACH_ITEM_NOTIFY_EVENT(EVENTNAME, ITEMEVENTNAME)\ {\ Func func(this, &GuiListControl::OnItemNotifyEvent);\ helper->EVENTNAME##Handler = style->GetEventReceiver()->EVENTNAME.AttachFunction(\ Curry(Curry(func)(ITEMEVENTNAME))(style)\ );\ }\ void GuiListControl::AttachItemEvents(ItemStyle* style) { vint index=visibleStyles.Keys().IndexOf(style); if(index==-1) { Ptr helper=new VisibleStyleHelper; visibleStyles.Add(style, helper); ATTACH_ITEM_MOUSE_EVENT(leftButtonDown, ItemLeftButtonDown); ATTACH_ITEM_MOUSE_EVENT(leftButtonUp, ItemLeftButtonUp); ATTACH_ITEM_MOUSE_EVENT(leftButtonDoubleClick, ItemLeftButtonDoubleClick); ATTACH_ITEM_MOUSE_EVENT(middleButtonDown, ItemMiddleButtonDown); ATTACH_ITEM_MOUSE_EVENT(middleButtonUp, ItemMiddleButtonUp); ATTACH_ITEM_MOUSE_EVENT(middleButtonDoubleClick, ItemMiddleButtonDoubleClick); ATTACH_ITEM_MOUSE_EVENT(rightButtonDown, ItemRightButtonDown); ATTACH_ITEM_MOUSE_EVENT(rightButtonUp, ItemRightButtonUp); ATTACH_ITEM_MOUSE_EVENT(rightButtonDoubleClick, ItemRightButtonDoubleClick); ATTACH_ITEM_MOUSE_EVENT(mouseMove, ItemMouseMove); ATTACH_ITEM_NOTIFY_EVENT(mouseEnter, ItemMouseEnter); ATTACH_ITEM_NOTIFY_EVENT(mouseLeave, ItemMouseLeave); } } #undef ATTACH_ITEM_MOUSE_EVENT #undef ATTACH_ITEM_NOTIFY_EVENT #define DETACH_ITEM_EVENT(EVENTNAME) style->GetEventReceiver()->EVENTNAME.Detach(helper->EVENTNAME##Handler) void GuiListControl::DetachItemEvents(ItemStyle* style) { vint index=visibleStyles.Keys().IndexOf(style); if(index!=-1) { Ptr helper=visibleStyles.Values().Get(index); visibleStyles.Remove(style); DETACH_ITEM_EVENT(leftButtonDown); DETACH_ITEM_EVENT(leftButtonUp); DETACH_ITEM_EVENT(leftButtonDoubleClick); DETACH_ITEM_EVENT(middleButtonDown); DETACH_ITEM_EVENT(middleButtonUp); DETACH_ITEM_EVENT(middleButtonDoubleClick); DETACH_ITEM_EVENT(rightButtonDown); DETACH_ITEM_EVENT(rightButtonUp); DETACH_ITEM_EVENT(rightButtonDoubleClick); DETACH_ITEM_EVENT(mouseMove); DETACH_ITEM_EVENT(mouseEnter); DETACH_ITEM_EVENT(mouseLeave); } } #undef DETACH_ITEM_EVENT GuiListControl::GuiListControl(theme::ThemeName themeName, IItemProvider* _itemProvider, bool acceptFocus) :GuiScrollView(themeName) , itemProvider(_itemProvider) { FontChanged.AttachMethod(this, &GuiListControl::OnFontChanged); ContextChanged.AttachMethod(this, &GuiListControl::OnContextChanged); VisuallyEnabledChanged.AttachMethod(this, &GuiListControl::OnVisuallyEnabledChanged); containerComposition->BoundsChanged.AttachMethod(this, &GuiListControl::OnClientBoundsChanged); ItemTemplateChanged.SetAssociatedComposition(boundsComposition); ArrangerChanged.SetAssociatedComposition(boundsComposition); AxisChanged.SetAssociatedComposition(boundsComposition); AdoptedSizeInvalidated.SetAssociatedComposition(boundsComposition); ItemLeftButtonDown.SetAssociatedComposition(boundsComposition); ItemLeftButtonUp.SetAssociatedComposition(boundsComposition); ItemLeftButtonDoubleClick.SetAssociatedComposition(boundsComposition); ItemMiddleButtonDown.SetAssociatedComposition(boundsComposition); ItemMiddleButtonUp.SetAssociatedComposition(boundsComposition); ItemMiddleButtonDoubleClick.SetAssociatedComposition(boundsComposition); ItemRightButtonDown.SetAssociatedComposition(boundsComposition); ItemRightButtonUp.SetAssociatedComposition(boundsComposition); ItemRightButtonDoubleClick.SetAssociatedComposition(boundsComposition); ItemMouseMove.SetAssociatedComposition(boundsComposition); ItemMouseEnter.SetAssociatedComposition(boundsComposition); ItemMouseLeave.SetAssociatedComposition(boundsComposition); callback = new ItemCallback(this); itemProvider->AttachCallback(callback.Obj()); axis = new GuiDefaultAxis; if (acceptFocus) { boundsComposition->GetEventReceiver()->leftButtonDown.AttachMethod(this, &GuiListControl::OnBoundsMouseButtonDown); boundsComposition->GetEventReceiver()->middleButtonDown.AttachMethod(this, &GuiListControl::OnBoundsMouseButtonDown); boundsComposition->GetEventReceiver()->rightButtonDown.AttachMethod(this, &GuiListControl::OnBoundsMouseButtonDown); SetFocusableComposition(boundsComposition); } } GuiListControl::~GuiListControl() { if(itemArranger) { itemProvider->DetachCallback(itemArranger.Obj()); } callback->ClearCache(); itemStyleProperty = {}; itemArranger = nullptr; } GuiListControl::IItemProvider* GuiListControl::GetItemProvider() { return itemProvider.Obj(); } GuiListControl::ItemStyleProperty GuiListControl::GetItemTemplate() { return itemStyleProperty; } void GuiListControl::SetItemTemplate(ItemStyleProperty value) { SetStyleAndArranger(value, itemArranger); ItemTemplateChanged.Execute(GetNotifyEventArguments()); } GuiListControl::IItemArranger* GuiListControl::GetArranger() { return itemArranger.Obj(); } void GuiListControl::SetArranger(Ptr value) { SetStyleAndArranger(itemStyleProperty, value); ArrangerChanged.Execute(GetNotifyEventArguments()); } compositions::IGuiAxis* GuiListControl::GetAxis() { return axis.Obj(); } void GuiListControl::SetAxis(Ptr value) { Ptr old = axis; axis = value; SetStyleAndArranger(itemStyleProperty, itemArranger); AxisChanged.Execute(GetNotifyEventArguments()); } bool GuiListControl::EnsureItemVisible(vint itemIndex) { if (itemIndex < 0 || itemIndex >= itemProvider->Count()) { return false; } return itemArranger ? itemArranger->EnsureItemVisible(itemIndex) : false; } Size GuiListControl::GetAdoptedSize(Size expectedSize) { if (itemArranger) { Size controlSize = boundsComposition->GetBounds().GetSize(); Size viewSize = containerComposition->GetBounds().GetSize(); vint x = controlSize.x - viewSize.x; vint y = controlSize.y - viewSize.y; Size expectedViewSize(expectedSize.x - x, expectedSize.y - y); if (axis) { expectedViewSize = axis->RealSizeToVirtualSize(expectedViewSize); } Size adoptedViewSize = itemArranger->GetAdoptedSize(expectedViewSize); if (axis) { adoptedViewSize = axis->VirtualSizeToRealSize(adoptedViewSize); } return Size(adoptedViewSize.x + x, adoptedViewSize.y + y); } return expectedSize; } bool GuiListControl::GetDisplayItemBackground() { return displayItemBackground; } void GuiListControl::SetDisplayItemBackground(bool value) { if (displayItemBackground != value) { displayItemBackground = value; SetStyleAndArranger(itemStyleProperty, itemArranger); } } /*********************************************************************** GuiSelectableListControl ***********************************************************************/ void GuiSelectableListControl::NotifySelectionChanged() { SelectionChanged.Execute(GetNotifyEventArguments()); } void GuiSelectableListControl::OnItemModified(vint start, vint count, vint newCount) { GuiListControl::OnItemModified(start, count, newCount); if(count!=newCount) { ClearSelection(); } } void GuiSelectableListControl::OnStyleInstalled(vint itemIndex, ItemStyle* style) { GuiListControl::OnStyleInstalled(itemIndex, style); style->SetSelected(selectedItems.Contains(itemIndex)); } void GuiSelectableListControl::OnItemSelectionChanged(vint itemIndex, bool value) { if(auto style = itemArranger->GetVisibleStyle(itemIndex)) { style->SetSelected(value); } } void GuiSelectableListControl::OnItemSelectionCleared() { FOREACH(ItemStyle*, style, visibleStyles.Keys()) { style->SetSelected(false); } } void GuiSelectableListControl::OnItemLeftButtonDown(compositions::GuiGraphicsComposition* sender, compositions::GuiItemMouseEventArgs& arguments) { if(GetVisuallyEnabled()) { SelectItemsByClick(arguments.itemIndex, arguments.ctrl, arguments.shift, true); } } void GuiSelectableListControl::OnItemRightButtonDown(compositions::GuiGraphicsComposition* sender, compositions::GuiItemMouseEventArgs& arguments) { if(GetVisuallyEnabled()) { SelectItemsByClick(arguments.itemIndex, arguments.ctrl, arguments.shift, false); } } void GuiSelectableListControl::NormalizeSelectedItemIndexStartEnd() { if(selectedItemIndexStart<0 || selectedItemIndexStart>=itemProvider->Count()) { selectedItemIndexStart=0; } if(selectedItemIndexEnd<0 || selectedItemIndexEnd>=itemProvider->Count()) { selectedItemIndexEnd=0; } } void GuiSelectableListControl::SetMultipleItemsSelectedSilently(vint start, vint end, bool selected) { if(start>end) { vint temp=start; start=end; end=temp; } vint count=itemProvider->Count(); if(start<0) start=0; if(end>=count) end=count-1; for(vint i=start;i<=end;i++) { if(selected) { if(!selectedItems.Contains(i)) { selectedItems.Add(i); } } else { selectedItems.Remove(i); } OnItemSelectionChanged(i, selected); } } void GuiSelectableListControl::OnKeyDown(compositions::GuiGraphicsComposition* sender, compositions::GuiKeyEventArgs& arguments) { if(GetVisuallyEnabled()) { if(SelectItemsByKey(arguments.code, arguments.ctrl, arguments.shift)) { arguments.handled=true; } } } GuiSelectableListControl::GuiSelectableListControl(theme::ThemeName themeName, IItemProvider* _itemProvider) :GuiListControl(themeName, _itemProvider, true) ,multiSelect(false) ,selectedItemIndexStart(-1) ,selectedItemIndexEnd(-1) { SelectionChanged.SetAssociatedComposition(boundsComposition); ItemLeftButtonDown.AttachMethod(this, &GuiSelectableListControl::OnItemLeftButtonDown); ItemRightButtonDown.AttachMethod(this, &GuiSelectableListControl::OnItemRightButtonDown); if(focusableComposition) { focusableComposition->GetEventReceiver()->keyDown.AttachMethod(this, &GuiSelectableListControl::OnKeyDown); } } GuiSelectableListControl::~GuiSelectableListControl() { } bool GuiSelectableListControl::GetMultiSelect() { return multiSelect; } void GuiSelectableListControl::SetMultiSelect(bool value) { if (multiSelect != value) { multiSelect = value; ClearSelection(); } } const collections::SortedList& GuiSelectableListControl::GetSelectedItems() { return selectedItems; } vint GuiSelectableListControl::GetSelectedItemIndex() { return selectedItems.Count() == 1 ? selectedItems[0] : -1; } WString GuiSelectableListControl::GetSelectedItemText() { vint index = GetSelectedItemIndex(); if (index != -1) { return itemProvider->GetTextValue(index); } return L""; } bool GuiSelectableListControl::GetSelected(vint itemIndex) { return selectedItems.Contains(itemIndex); } void GuiSelectableListControl::SetSelected(vint itemIndex, bool value) { if(value) { if(!selectedItems.Contains(itemIndex)) { if(!multiSelect) { selectedItems.Clear(); OnItemSelectionCleared(); } selectedItems.Add(itemIndex); OnItemSelectionChanged(itemIndex, value); NotifySelectionChanged(); } } else { if(selectedItems.Remove(itemIndex)) { OnItemSelectionChanged(itemIndex, value); NotifySelectionChanged(); } } } bool GuiSelectableListControl::SelectItemsByClick(vint itemIndex, bool ctrl, bool shift, bool leftButton) { NormalizeSelectedItemIndexStartEnd(); if(0<=itemIndex && itemIndexCount()) { if(!leftButton) { if(selectedItems.Contains(itemIndex)) { return true; } } if(!multiSelect) { shift=false; ctrl=false; } if(shift) { if(!ctrl) { SetMultipleItemsSelectedSilently(selectedItemIndexStart, selectedItemIndexEnd, false); } selectedItemIndexEnd=itemIndex; SetMultipleItemsSelectedSilently(selectedItemIndexStart, selectedItemIndexEnd, true); NotifySelectionChanged(); } else { if(ctrl) { vint index=selectedItems.IndexOf(itemIndex); if(index==-1) { selectedItems.Add(itemIndex); } else { selectedItems.RemoveAt(index); } OnItemSelectionChanged(itemIndex, index==-1); NotifySelectionChanged(); } else { selectedItems.Clear(); OnItemSelectionCleared(); selectedItems.Add(itemIndex); OnItemSelectionChanged(itemIndex, true); NotifySelectionChanged(); } selectedItemIndexStart=itemIndex; selectedItemIndexEnd=itemIndex; } return true; } return false; } bool GuiSelectableListControl::SelectItemsByKey(vint code, bool ctrl, bool shift) { if(!GetArranger()) return false; NormalizeSelectedItemIndexStartEnd(); KeyDirection keyDirection=KeyDirection::Up; switch(code) { case VKEY_UP: keyDirection=KeyDirection::Up; break; case VKEY_DOWN: keyDirection=KeyDirection::Down; break; case VKEY_LEFT: keyDirection=KeyDirection::Left; break; case VKEY_RIGHT: keyDirection=KeyDirection::Right; break; case VKEY_HOME: keyDirection=KeyDirection::Home; break; case VKEY_END: keyDirection=KeyDirection::End; break; case VKEY_PRIOR: keyDirection=KeyDirection::PageUp; break; case VKEY_NEXT: keyDirection=KeyDirection::PageDown; break; default: return false; } if(GetAxis()) { keyDirection=GetAxis()->RealKeyDirectionToVirtualKeyDirection(keyDirection); } vint itemIndex=GetArranger()->FindItem(selectedItemIndexEnd, keyDirection); if(SelectItemsByClick(itemIndex, ctrl, shift, true)) { return EnsureItemVisible(itemIndex); } else { return false; } } void GuiSelectableListControl::ClearSelection() { if(selectedItems.Count()>0) { selectedItems.Clear(); OnItemSelectionCleared(); NotifySelectionChanged(); } } namespace list { /*********************************************************************** ItemProviderBase ***********************************************************************/ void ItemProviderBase::InvokeOnItemModified(vint start, vint count, vint newCount) { for (vint i = 0; i < callbacks.Count(); i++) { callbacks[i]->OnItemModified(start, count, newCount); } } ItemProviderBase::ItemProviderBase() { } ItemProviderBase::~ItemProviderBase() { for(vint i=0;iOnAttached(0); } } bool ItemProviderBase::AttachCallback(GuiListControl::IItemProviderCallback* value) { if(callbacks.Contains(value)) { return false; } else { callbacks.Add(value); value->OnAttached(this); return true; } } bool ItemProviderBase::DetachCallback(GuiListControl::IItemProviderCallback* value) { vint index=callbacks.IndexOf(value); if(index==-1) { return false; } else { value->OnAttached(0); callbacks.Remove(value); return true; } } void ItemProviderBase::PushEditing() { editingCounter++; } bool ItemProviderBase::PopEditing() { if (editingCounter == 0)return false; editingCounter--; return true; } bool ItemProviderBase::IsEditing() { return editingCounter > 0; } } } } } /*********************************************************************** .\CONTROLS\TEMPLATES\GUICONTROLTEMPLATES.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace templates { using namespace collections; using namespace controls; using namespace compositions; using namespace elements; /*********************************************************************** GuiTemplate ***********************************************************************/ GuiTemplate_PROPERTIES(GUI_TEMPLATE_PROPERTY_IMPL) controls::GuiControlHost* GuiTemplate::GetControlHostForInstance() { return GetRelatedControlHost(); } void GuiTemplate::OnParentLineChanged() { GuiBoundsComposition::OnParentLineChanged(); OnControlHostForInstanceChanged(); } GuiTemplate::GuiTemplate() { GuiTemplate_PROPERTIES(GUI_TEMPLATE_PROPERTY_EVENT_INIT) } GuiTemplate::~GuiTemplate() { FinalizeInstanceRecursively(this); } /*********************************************************************** Item GuiListItemTemplate ***********************************************************************/ void GuiListItemTemplate::OnInitialize() { } GuiListItemTemplate_PROPERTIES(GUI_TEMPLATE_PROPERTY_IMPL) GuiListItemTemplate::GuiListItemTemplate() { GuiListItemTemplate_PROPERTIES(GUI_TEMPLATE_PROPERTY_EVENT_INIT) } GuiListItemTemplate::~GuiListItemTemplate() { FinalizeAggregation(); } void GuiListItemTemplate::BeginEditListItem() { listControl->GetItemProvider()->PushEditing(); } void GuiListItemTemplate::EndEditListItem() { CHECK_ERROR(listControl->GetItemProvider()->PopEditing(), L"GuiListItemTemplate::EndEditListItem()#BeginEditListItem and EndEditListItem calls are not paired."); } void GuiListItemTemplate::Initialize(controls::GuiListControl* _listControl) { CHECK_ERROR(listControl == nullptr, L"GuiListItemTemplate::Initialize(GuiListControl*)#This function can only be called once."); listControl = _listControl; OnInitialize(); } /*********************************************************************** Template Declarations ***********************************************************************/ GUI_CONTROL_TEMPLATE_DECL(GUI_TEMPLATE_CLASS_IMPL) GUI_ITEM_TEMPLATE_DECL(GUI_TEMPLATE_CLASS_IMPL) } } } /*********************************************************************** .\CONTROLS\LISTCONTROLPACKAGE\GUILISTCONTROLITEMARRANGERS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace collections; using namespace elements; using namespace compositions; namespace list { /*********************************************************************** RangedItemArrangerBase ***********************************************************************/ void RangedItemArrangerBase::InvalidateAdoptedSize() { if (listControl) { listControl->AdoptedSizeInvalidated.Execute(listControl->GetNotifyEventArguments()); } } vint RangedItemArrangerBase::CalculateAdoptedSize(vint expectedSize, vint count, vint itemSize) { vint visibleCount = expectedSize / itemSize; if (count < visibleCount) { visibleCount = count; } else if (count > visibleCount) { vint deltaA = expectedSize - count * itemSize; vint deltaB = itemSize - deltaA; if (deltaB < deltaA) { visibleCount++; } } return visibleCount * itemSize; } RangedItemArrangerBase::ItemStyleRecord RangedItemArrangerBase::CreateStyle(vint index) { GuiSelectableButton* backgroundButton = nullptr; if (listControl->GetDisplayItemBackground()) { backgroundButton = new GuiSelectableButton(theme::ThemeName::ListItemBackground); if (auto style = listControl->GetControlTemplateObject(true)->GetBackgroundTemplate()) { backgroundButton->SetControlTemplate(style); } backgroundButton->SetAutoSelection(false); } auto itemStyle = callback->RequestItem(index, backgroundButton->GetBoundsComposition()); if (backgroundButton) { itemStyle->SetAlignmentToParent(Margin(0, 0, 0, 0)); itemStyle->SelectedChanged.AttachLambda([=](GuiGraphicsComposition* sender, GuiEventArgs& arguments) { backgroundButton->SetSelected(itemStyle->GetSelected()); }); backgroundButton->SetSelected(itemStyle->GetSelected()); backgroundButton->GetContainerComposition()->AddChild(itemStyle); } return { itemStyle, backgroundButton }; } void RangedItemArrangerBase::DeleteStyle(ItemStyleRecord style) { callback->ReleaseItem(style.key); if (style.value) { SafeDeleteControl(style.value); } } compositions::GuiBoundsComposition* RangedItemArrangerBase::GetStyleBounds(ItemStyleRecord style) { return style.value ? style.value->GetBoundsComposition() : style.key; } void RangedItemArrangerBase::ClearStyles() { startIndex = 0; if (callback) { for (vint i = 0; i < visibleStyles.Count(); i++) { DeleteStyle(visibleStyles[i]); } } visibleStyles.Clear(); viewBounds = Rect(0, 0, 0, 0); InvalidateItemSizeCache(); InvalidateAdoptedSize(); } void RangedItemArrangerBase::OnViewChangedInternal(Rect oldBounds, Rect newBounds) { vint endIndex = startIndex + visibleStyles.Count() - 1; vint newStartIndex = 0; vint itemCount = itemProvider->Count(); BeginPlaceItem(true, newBounds, newStartIndex); if (newStartIndex < 0) newStartIndex = 0; StyleList newVisibleStyles; for (vint i = newStartIndex; i < itemCount; i++) { auto style = startIndex <= i && i <= endIndex ? visibleStyles[i - startIndex] : CreateStyle(i) ; newVisibleStyles.Add(style); Rect bounds; Margin alignmentToParent; PlaceItem(true, i, style, newBounds, bounds, alignmentToParent); if (IsItemOutOfViewBounds(i, style, bounds, newBounds)) { break; } bounds.x1 -= newBounds.x1; bounds.x2 -= newBounds.x1; bounds.y1 -= newBounds.y1; bounds.y2 -= newBounds.y1; } vint newEndIndex = newStartIndex + newVisibleStyles.Count() - 1; for (vint i = 0; i < visibleStyles.Count(); i++) { vint index = startIndex + i; if (index < newStartIndex || index > newEndIndex) { DeleteStyle(visibleStyles[i]); } } CopyFrom(visibleStyles, newVisibleStyles); if (EndPlaceItem(true, newBounds, newStartIndex)) { callback->OnTotalSizeChanged(); InvalidateAdoptedSize(); } startIndex = newStartIndex; } void RangedItemArrangerBase::RearrangeItemBounds() { vint newStartIndex = startIndex; BeginPlaceItem(false, viewBounds, newStartIndex); for (vint i = 0; i < visibleStyles.Count(); i++) { auto style = visibleStyles[i]; Rect bounds; Margin alignmentToParent(-1, -1, -1, -1); PlaceItem(false, startIndex + i, style, viewBounds, bounds, alignmentToParent); bounds.x1 -= viewBounds.x1; bounds.x2 -= viewBounds.x1; bounds.y1 -= viewBounds.y1; bounds.y2 -= viewBounds.y1; callback->SetStyleAlignmentToParent(GetStyleBounds(style), alignmentToParent); callback->SetStyleBounds(GetStyleBounds(style), bounds); } EndPlaceItem(false, viewBounds, startIndex); } RangedItemArrangerBase::RangedItemArrangerBase() { } RangedItemArrangerBase::~RangedItemArrangerBase() { } void RangedItemArrangerBase::OnAttached(GuiListControl::IItemProvider* provider) { itemProvider = provider; if (provider) { OnItemModified(0, 0, provider->Count()); } } void RangedItemArrangerBase::OnItemModified(vint start, vint count, vint newCount) { if (callback && !itemProvider->IsEditing()) { suppressOnViewChanged = true; { vint visibleCount = visibleStyles.Count(); vint itemCount = itemProvider->Count(); SortedList reusedStyles; for (vint i = 0; i < visibleCount; i++) { vint index = startIndex + i; if (index >= itemCount) { break; } vint oldIndex = -1; if (index < start) { oldIndex = index; } else if (index >= start + newCount) { oldIndex = index - newCount + count; } if (oldIndex != -1) { if (oldIndex >= startIndex && oldIndex < startIndex + visibleCount) { auto style = visibleStyles[oldIndex - startIndex]; reusedStyles.Add(style); visibleStyles.Add(style); } else { oldIndex = -1; } } if (oldIndex == -1) { visibleStyles.Add(CreateStyle(index)); } } for (vint i = 0; i < visibleCount; i++) { auto style = visibleStyles[i]; if (!reusedStyles.Contains(style)) { DeleteStyle(style); } } visibleStyles.RemoveRange(0, visibleCount); for (vint i = 0; i < visibleStyles.Count(); i++) { visibleStyles[i].key->SetIndex(startIndex + i); } } suppressOnViewChanged = false; callback->OnTotalSizeChanged(); callback->SetViewLocation(viewBounds.LeftTop()); InvalidateAdoptedSize(); } } void RangedItemArrangerBase::AttachListControl(GuiListControl* value) { listControl = value; InvalidateAdoptedSize(); } void RangedItemArrangerBase::DetachListControl() { listControl = 0; } GuiListControl::IItemArrangerCallback* RangedItemArrangerBase::GetCallback() { return callback; } void RangedItemArrangerBase::SetCallback(GuiListControl::IItemArrangerCallback* value) { if (callback != value) { ClearStyles(); callback = value; } } Size RangedItemArrangerBase::GetTotalSize() { if (callback) { return OnCalculateTotalSize(); } else { return Size(0, 0); } } GuiListControl::ItemStyle* RangedItemArrangerBase::GetVisibleStyle(vint itemIndex) { if (startIndex <= itemIndex && itemIndex < startIndex + visibleStyles.Count()) { return visibleStyles[itemIndex - startIndex].key; } else { return 0; } } vint RangedItemArrangerBase::GetVisibleIndex(GuiListControl::ItemStyle* style) { for (vint i = 0; i < visibleStyles.Count(); i++) { if (visibleStyles[i].key == style) { return i + startIndex; } } return -1; } void RangedItemArrangerBase::ReloadVisibleStyles() { ClearStyles(); } void RangedItemArrangerBase::OnViewChanged(Rect bounds) { if (!suppressOnViewChanged) { suppressOnViewChanged = true; Rect oldBounds = viewBounds; viewBounds = bounds; if (callback) { OnViewChangedInternal(oldBounds, viewBounds); RearrangeItemBounds(); } suppressOnViewChanged = false; } } /*********************************************************************** FixedHeightItemArranger ***********************************************************************/ vint FixedHeightItemArranger::GetWidth() { return -1; } vint FixedHeightItemArranger::GetYOffset() { return 0; } void FixedHeightItemArranger::BeginPlaceItem(bool forMoving, Rect newBounds, vint& newStartIndex) { pi_width = GetWidth(); if (forMoving) { pim_rowHeight = rowHeight; newStartIndex = (newBounds.Top() - GetYOffset()) / rowHeight; } } void FixedHeightItemArranger::PlaceItem(bool forMoving, vint index, ItemStyleRecord style, Rect viewBounds, Rect& bounds, Margin& alignmentToParent) { vint top = GetYOffset() + index * rowHeight; if (pi_width == -1) { alignmentToParent = Margin(0, -1, 0, -1); bounds = Rect(Point(0, top), Size(0, rowHeight)); } else { alignmentToParent = Margin(-1, -1, -1, -1); bounds = Rect(Point(0, top), Size(pi_width, rowHeight)); } if (forMoving) { vint styleHeight = callback->GetStylePreferredSize(GetStyleBounds(style)).y; if (pim_rowHeight < styleHeight) { pim_rowHeight = styleHeight; } } } bool FixedHeightItemArranger::IsItemOutOfViewBounds(vint index, ItemStyleRecord style, Rect bounds, Rect viewBounds) { return bounds.Top() >= viewBounds.Bottom(); } bool FixedHeightItemArranger::EndPlaceItem(bool forMoving, Rect newBounds, vint newStartIndex) { if (forMoving) { if (pim_rowHeight != rowHeight) { vint offset = (pim_rowHeight - rowHeight) * newStartIndex; rowHeight = pim_rowHeight; callback->SetViewLocation(Point(0, newBounds.Top() + offset)); return true; } } return false; } void FixedHeightItemArranger::InvalidateItemSizeCache() { rowHeight = 1; } Size FixedHeightItemArranger::OnCalculateTotalSize() { vint width = GetWidth(); if (width < 0) width = 0; return Size(width, rowHeight * itemProvider->Count() + GetYOffset()); } FixedHeightItemArranger::FixedHeightItemArranger() { } FixedHeightItemArranger::~FixedHeightItemArranger() { } vint FixedHeightItemArranger::FindItem(vint itemIndex, compositions::KeyDirection key) { vint count = itemProvider->Count(); if (count == 0) return -1; vint groupCount = viewBounds.Height() / rowHeight; if (groupCount == 0) groupCount = 1; switch (key) { case KeyDirection::Up: itemIndex--; break; case KeyDirection::Down: itemIndex++; break; case KeyDirection::Home: itemIndex = 0; break; case KeyDirection::End: itemIndex = count; break; case KeyDirection::PageUp: itemIndex -= groupCount; break; case KeyDirection::PageDown: itemIndex += groupCount; break; default: return -1; } if (itemIndex < 0) return 0; else if (itemIndex >= count) return count - 1; else return itemIndex; } bool FixedHeightItemArranger::EnsureItemVisible(vint itemIndex) { if (callback) { if (itemIndex < 0 || itemIndex >= itemProvider->Count()) { return false; } while (true) { vint yOffset = GetYOffset(); vint top = itemIndex*rowHeight; vint bottom = top + rowHeight + yOffset; if (viewBounds.Height() < rowHeight) { if (viewBounds.Top() < bottom && top < viewBounds.Bottom()) { break; } } Point location = viewBounds.LeftTop(); if (top < viewBounds.Top()) { location.y = top; } else if (viewBounds.Bottom() < bottom) { location.y = bottom - viewBounds.Height(); } else { break; } callback->SetViewLocation(location); } return true; } return false; } Size FixedHeightItemArranger::GetAdoptedSize(Size expectedSize) { if (itemProvider) { vint yOffset = GetYOffset(); vint y = expectedSize.y - yOffset; vint itemCount = itemProvider->Count(); return Size(expectedSize.x, yOffset + CalculateAdoptedSize(y, itemCount, rowHeight)); } return expectedSize; } /*********************************************************************** FixedSizeMultiColumnItemArranger ***********************************************************************/ void FixedSizeMultiColumnItemArranger::BeginPlaceItem(bool forMoving, Rect newBounds, vint& newStartIndex) { if (forMoving) { pim_itemSize = itemSize; vint rows = newBounds.Top() / itemSize.y; if (rows < 0) rows = 0; vint cols = newBounds.Width() / itemSize.x; if (cols < 1) cols = 1; newStartIndex = rows * cols; } } void FixedSizeMultiColumnItemArranger::PlaceItem(bool forMoving, vint index, ItemStyleRecord style, Rect viewBounds, Rect& bounds, Margin& alignmentToParent) { vint rowItems = viewBounds.Width() / itemSize.x; if (rowItems < 1) rowItems = 1; vint row = index / rowItems; vint col = index % rowItems; bounds = Rect(Point(col * itemSize.x, row * itemSize.y), itemSize); if (forMoving) { Size styleSize = callback->GetStylePreferredSize(GetStyleBounds(style)); if (pim_itemSize.x < styleSize.x) pim_itemSize.x = styleSize.x; if (pim_itemSize.y < styleSize.y) pim_itemSize.y = styleSize.y; } } bool FixedSizeMultiColumnItemArranger::IsItemOutOfViewBounds(vint index, ItemStyleRecord style, Rect bounds, Rect viewBounds) { return bounds.Top() >= viewBounds.Bottom(); } bool FixedSizeMultiColumnItemArranger::EndPlaceItem(bool forMoving, Rect newBounds, vint newStartIndex) { if (forMoving) { if (pim_itemSize != itemSize) { itemSize = pim_itemSize; return true; } } return false; } void FixedSizeMultiColumnItemArranger::CalculateRange(Size itemSize, Rect bounds, vint count, vint& start, vint& end) { vint startRow = bounds.Top() / itemSize.y; if (startRow < 0) startRow = 0; vint endRow = (bounds.Bottom() - 1) / itemSize.y; vint cols = bounds.Width() / itemSize.x; if (cols < 1) cols = 1; start = startRow*cols; end = (endRow + 1)*cols - 1; if (end >= count) end = count - 1; } void FixedSizeMultiColumnItemArranger::InvalidateItemSizeCache() { itemSize = Size(1, 1); } Size FixedSizeMultiColumnItemArranger::OnCalculateTotalSize() { vint rowItems = viewBounds.Width() / itemSize.x; if (rowItems < 1) rowItems = 1; vint rows = itemProvider->Count() / rowItems; if (itemProvider->Count() % rowItems) rows++; return Size(itemSize.x * rowItems, itemSize.y*rows); } FixedSizeMultiColumnItemArranger::FixedSizeMultiColumnItemArranger() { } FixedSizeMultiColumnItemArranger::~FixedSizeMultiColumnItemArranger() { } vint FixedSizeMultiColumnItemArranger::FindItem(vint itemIndex, compositions::KeyDirection key) { vint count = itemProvider->Count(); vint columnCount = viewBounds.Width() / itemSize.x; if (columnCount == 0) columnCount = 1; vint rowCount = viewBounds.Height() / itemSize.y; if (rowCount == 0) rowCount = 1; switch (key) { case KeyDirection::Up: itemIndex -= columnCount; break; case KeyDirection::Down: itemIndex += columnCount; break; case KeyDirection::Left: itemIndex--; break; case KeyDirection::Right: itemIndex++; break; case KeyDirection::Home: itemIndex = 0; break; case KeyDirection::End: itemIndex = count; break; case KeyDirection::PageUp: itemIndex -= columnCount*rowCount; break; case KeyDirection::PageDown: itemIndex += columnCount*rowCount; break; case KeyDirection::PageLeft: itemIndex -= itemIndex%columnCount; break; case KeyDirection::PageRight: itemIndex += columnCount - itemIndex%columnCount - 1; break; default: return -1; } if (itemIndex < 0) return 0; else if (itemIndex >= count) return count - 1; else return itemIndex; } bool FixedSizeMultiColumnItemArranger::EnsureItemVisible(vint itemIndex) { if (callback) { if (itemIndex < 0 || itemIndex >= itemProvider->Count()) { return false; } while (true) { vint rowHeight = itemSize.y; vint columnCount = viewBounds.Width() / itemSize.x; if (columnCount == 0) columnCount = 1; vint rowIndex = itemIndex / columnCount; vint top = rowIndex*rowHeight; vint bottom = top + rowHeight; if (viewBounds.Height() < rowHeight) { if (viewBounds.Top() < bottom && top < viewBounds.Bottom()) { break; } } Point location = viewBounds.LeftTop(); if (top < viewBounds.Top()) { location.y = top; } else if (viewBounds.Bottom() < bottom) { location.y = bottom - viewBounds.Height(); } else { break; } callback->SetViewLocation(location); } return true; } return false; } Size FixedSizeMultiColumnItemArranger::GetAdoptedSize(Size expectedSize) { if (itemProvider) { vint count = itemProvider->Count(); vint columnCount = viewBounds.Width() / itemSize.x; vint rowCount = viewBounds.Height() / itemSize.y; return Size( CalculateAdoptedSize(expectedSize.x, columnCount, itemSize.x), CalculateAdoptedSize(expectedSize.y, rowCount, itemSize.y) ); } return expectedSize; } /*********************************************************************** FixedHeightMultiColumnItemArranger ***********************************************************************/ void FixedHeightMultiColumnItemArranger::CalculateRange(vint itemHeight, Rect bounds, vint& rows, vint& startColumn) { rows = bounds.Height() / itemHeight; if (rows < 1) rows = 1; startColumn = bounds.Left() / bounds.Width(); } void FixedHeightMultiColumnItemArranger::BeginPlaceItem(bool forMoving, Rect newBounds, vint& newStartIndex) { pi_currentWidth = 0; pi_totalWidth = 0; if (forMoving) { pim_itemHeight = itemHeight; vint rows = newBounds.Height() / itemHeight; if (rows < 1) rows = 1; vint columns = newBounds.Left() / newBounds.Width(); newStartIndex = rows * columns; } } void FixedHeightMultiColumnItemArranger::PlaceItem(bool forMoving, vint index, ItemStyleRecord style, Rect viewBounds, Rect& bounds, Margin& alignmentToParent) { vint rows = viewBounds.Height() / itemHeight; if (rows < 1) rows = 1; vint row = index % rows; if (row == 0) { pi_totalWidth += pi_currentWidth; pi_currentWidth = 0; } Size styleSize = callback->GetStylePreferredSize(GetStyleBounds(style)); if (pi_currentWidth < styleSize.x) pi_currentWidth = styleSize.x; bounds = Rect(Point(pi_totalWidth + viewBounds.Left(), itemHeight * row), Size(0, 0)); if (forMoving) { if (pim_itemHeight < styleSize.y) pim_itemHeight = styleSize.y; } } bool FixedHeightMultiColumnItemArranger::IsItemOutOfViewBounds(vint index, ItemStyleRecord style, Rect bounds, Rect viewBounds) { return bounds.Left() >= viewBounds.Right(); } bool FixedHeightMultiColumnItemArranger::EndPlaceItem(bool forMoving, Rect newBounds, vint newStartIndex) { if (forMoving) { if (pim_itemHeight != itemHeight) { itemHeight = pim_itemHeight; return true; } } return false; } void FixedHeightMultiColumnItemArranger::InvalidateItemSizeCache() { itemHeight = 1; } Size FixedHeightMultiColumnItemArranger::OnCalculateTotalSize() { vint rows = viewBounds.Height() / itemHeight; if (rows < 1) rows = 1; vint columns = itemProvider->Count() / rows; if (itemProvider->Count() % rows) columns += 1; return Size(viewBounds.Width() * columns, 0); } FixedHeightMultiColumnItemArranger::FixedHeightMultiColumnItemArranger() :itemHeight(1) { } FixedHeightMultiColumnItemArranger::~FixedHeightMultiColumnItemArranger() { } vint FixedHeightMultiColumnItemArranger::FindItem(vint itemIndex, compositions::KeyDirection key) { vint count = itemProvider->Count(); vint groupCount = viewBounds.Height() / itemHeight; if (groupCount == 0) groupCount = 1; switch (key) { case KeyDirection::Up: itemIndex--; break; case KeyDirection::Down: itemIndex++; break; case KeyDirection::Left: itemIndex -= groupCount; break; case KeyDirection::Right: itemIndex += groupCount; break; case KeyDirection::Home: itemIndex = 0; break; case KeyDirection::End: itemIndex = count; break; case KeyDirection::PageUp: itemIndex -= itemIndex%groupCount; break; case KeyDirection::PageDown: itemIndex += groupCount - itemIndex%groupCount - 1; break; default: return -1; } if (itemIndex < 0) return 0; else if (itemIndex >= count) return count - 1; else return itemIndex; } bool FixedHeightMultiColumnItemArranger::EnsureItemVisible(vint itemIndex) { if (callback) { if (itemIndex < 0 || itemIndex >= itemProvider->Count()) { return false; } while (true) { vint rowCount = viewBounds.Height() / itemHeight; if (rowCount == 0) rowCount = 1; vint columnIndex = itemIndex / rowCount; vint minIndex = startIndex; vint maxIndex = startIndex + visibleStyles.Count() - 1; Point location = viewBounds.LeftTop(); if (minIndex <= itemIndex && itemIndex <= maxIndex) { Rect bounds = callback->GetStyleBounds(GetStyleBounds(visibleStyles[itemIndex - startIndex])); if (0 < bounds.Bottom() && bounds.Top() < viewBounds.Width() && bounds.Width() > viewBounds.Width()) { break; } else if (bounds.Left() < 0) { location.x -= viewBounds.Width(); } else if (bounds.Right() > viewBounds.Width()) { location.x += viewBounds.Width(); } else { break; } } else if (columnIndex < minIndex / rowCount) { location.x -= viewBounds.Width(); } else if (columnIndex >= maxIndex / rowCount) { location.x += viewBounds.Width(); } else { break; } callback->SetViewLocation(location); } return true; } return false; } Size FixedHeightMultiColumnItemArranger::GetAdoptedSize(Size expectedSize) { if (itemProvider) { vint count = itemProvider->Count(); return Size(expectedSize.x, CalculateAdoptedSize(expectedSize.y, count, itemHeight)); } return expectedSize; } } } } } /*********************************************************************** .\CONTROLS\LISTCONTROLPACKAGE\GUITEXTLISTCONTROLS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace collections; using namespace elements; using namespace compositions; using namespace reflection::description; namespace list { const wchar_t* const ITextItemView::Identifier = L"vl::presentation::controls::list::ITextItemView"; /*********************************************************************** DefaultTextListItemTemplate ***********************************************************************/ TemplateProperty DefaultTextListItemTemplate::CreateBulletStyle() { return {}; } void DefaultTextListItemTemplate::OnInitialize() { SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); textElement = GuiSolidLabelElement::Create(); textElement->SetAlignments(Alignment::Left, Alignment::Center); GuiBoundsComposition* textComposition = new GuiBoundsComposition; textComposition->SetOwnedElement(textElement); textComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElement); if (auto bulletStyleController = CreateBulletStyle()) { bulletButton = new GuiSelectableButton(theme::ThemeName::Unknown); bulletButton->SetControlTemplate(bulletStyleController); bulletButton->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); bulletButton->SelectedChanged.AttachMethod(this, &DefaultTextListItemTemplate::OnBulletSelectedChanged); GuiTableComposition* table = new GuiTableComposition; AddChild(table); table->SetAlignmentToParent(Margin(0, 0, 0, 0)); table->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); table->SetRowsAndColumns(1, 2); table->SetRowOption(0, GuiCellOption::PercentageOption(1.0)); table->SetColumnOption(0, GuiCellOption::MinSizeOption()); table->SetColumnOption(1, GuiCellOption::PercentageOption(1.0)); { GuiCellComposition* cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(0, 0, 1, 1); cell->AddChild(bulletButton->GetBoundsComposition()); } { GuiCellComposition* cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(0, 1, 1, 1); cell->AddChild(textComposition); textComposition->SetAlignmentToParent(Margin(0, 0, 0, 0)); } } else { AddChild(textComposition); textComposition->SetAlignmentToParent(Margin(5, 0, 0, 0)); } FontChanged.AttachMethod(this, &DefaultTextListItemTemplate::OnFontChanged); TextChanged.AttachMethod(this, &DefaultTextListItemTemplate::OnTextChanged); TextColorChanged.AttachMethod(this, &DefaultTextListItemTemplate::OnTextColorChanged); CheckedChanged.AttachMethod(this, &DefaultTextListItemTemplate::OnCheckedChanged); FontChanged.Execute(compositions::GuiEventArgs(this)); TextChanged.Execute(compositions::GuiEventArgs(this)); TextColorChanged.Execute(compositions::GuiEventArgs(this)); CheckedChanged.Execute(compositions::GuiEventArgs(this)); } void DefaultTextListItemTemplate::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { textElement->SetFont(GetFont()); } void DefaultTextListItemTemplate::OnTextChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { textElement->SetText(GetText()); } void DefaultTextListItemTemplate::OnTextColorChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { textElement->SetColor(GetTextColor()); } void DefaultTextListItemTemplate::OnCheckedChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if (bulletButton) { supressEdit = true; bulletButton->SetSelected(GetChecked()); supressEdit = false; } } void DefaultTextListItemTemplate::OnBulletSelectedChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if (!supressEdit) { if (auto textItemView = dynamic_cast(listControl->GetItemProvider()->RequestView(ITextItemView::Identifier))) { BeginEditListItem(); textItemView->SetChecked(GetIndex(), bulletButton->GetSelected()); EndEditListItem(); } } } DefaultTextListItemTemplate::DefaultTextListItemTemplate() { } DefaultTextListItemTemplate::~DefaultTextListItemTemplate() { } /*********************************************************************** DefaultCheckTextListItemTemplate ***********************************************************************/ TemplateProperty DefaultCheckTextListItemTemplate::CreateBulletStyle() { if (auto textList = dynamic_cast(listControl)) { auto style = textList->GetControlTemplateObject(true)->GetCheckBulletTemplate(); if (style) return style; } return theme::GetCurrentTheme()->CreateStyle(theme::ThemeName::CheckTextListItem); } /*********************************************************************** DefaultRadioTextListItemTemplate ***********************************************************************/ TemplateProperty DefaultRadioTextListItemTemplate::CreateBulletStyle() { if (auto textList = dynamic_cast(listControl)) { auto style = textList->GetControlTemplateObject(true)->GetRadioBulletTemplate(); if (style) return style; } return theme::GetCurrentTheme()->CreateStyle(theme::ThemeName::RadioTextListItem); } /*********************************************************************** TextItem ***********************************************************************/ TextItem::TextItem() :owner(0) , checked(false) { } TextItem::TextItem(const WString& _text, bool _checked) :owner(0) , text(_text) , checked(_checked) { } TextItem::~TextItem() { } bool TextItem::operator==(const TextItem& value)const { return text==value.text; } bool TextItem::operator!=(const TextItem& value)const { return text!=value.text; } const WString& TextItem::GetText() { return text; } void TextItem::SetText(const WString& value) { if (text != value) { text = value; if (owner) { vint index = owner->IndexOf(this); owner->InvokeOnItemModified(index, 1, 1); } } } bool TextItem::GetChecked() { return checked; } void TextItem::SetChecked(bool value) { if (checked != value) { checked = value; if (owner) { vint index = owner->IndexOf(this); owner->InvokeOnItemModified(index, 1, 1); GuiItemEventArgs arguments; arguments.itemIndex = index; owner->listControl->ItemChecked.Execute(arguments); } } } /*********************************************************************** TextItemProvider ***********************************************************************/ void TextItemProvider::AfterInsert(vint item, const Ptr& value) { ListProvider>::AfterInsert(item, value); value->owner = this; } void TextItemProvider::BeforeRemove(vint item, const Ptr& value) { value->owner = 0; ListProvider>::BeforeRemove(item, value); } WString TextItemProvider::GetTextValue(vint itemIndex) { return Get(itemIndex)->GetText(); } description::Value TextItemProvider::GetBindingValue(vint itemIndex) { return Value::From(Get(itemIndex)); } bool TextItemProvider::GetChecked(vint itemIndex) { return Get(itemIndex)->GetChecked(); } void TextItemProvider::SetChecked(vint itemIndex, bool value) { return Get(itemIndex)->SetChecked(value); } TextItemProvider::TextItemProvider() :listControl(0) { } TextItemProvider::~TextItemProvider() { } IDescriptable* TextItemProvider::RequestView(const WString& identifier) { if (identifier == ITextItemView::Identifier) { return (ITextItemView*)this; } else { return nullptr; } } } /*********************************************************************** GuiTextList ***********************************************************************/ void GuiVirtualTextList::BeforeControlTemplateUninstalled_() { } void GuiVirtualTextList::AfterControlTemplateInstalled_(bool initialize) { } void GuiVirtualTextList::OnStyleInstalled(vint itemIndex, ItemStyle* style) { GuiSelectableListControl::OnStyleInstalled(itemIndex, style); if (auto textItemStyle = dynamic_cast(style)) { textItemStyle->SetTextColor(GetControlTemplateObject(true)->GetTextColor()); if (auto textItemView = dynamic_cast(itemProvider->RequestView(list::ITextItemView::Identifier))) { textItemStyle->SetChecked(textItemView->GetChecked(itemIndex)); } } } void GuiVirtualTextList::OnItemTemplateChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { view = TextListView::Unknown; } GuiVirtualTextList::GuiVirtualTextList(theme::ThemeName themeName, GuiListControl::IItemProvider* _itemProvider) :GuiSelectableListControl(themeName, _itemProvider) { ItemTemplateChanged.AttachMethod(this, &GuiVirtualTextList::OnItemTemplateChanged); ItemChecked.SetAssociatedComposition(boundsComposition); SetView(TextListView::Text); } GuiVirtualTextList::~GuiVirtualTextList() { } TextListView GuiVirtualTextList::GetView() { return view; } void GuiVirtualTextList::SetView(TextListView _view) { switch (_view) { case TextListView::Text: SetStyleAndArranger( [](const Value&) { return new list::DefaultTextListItemTemplate; }, new list::FixedHeightItemArranger ); break; case TextListView::Check: SetStyleAndArranger( [](const Value&) { return new list::DefaultCheckTextListItemTemplate; }, new list::FixedHeightItemArranger ); break; case TextListView::Radio: SetStyleAndArranger( [](const Value&) { return new list::DefaultRadioTextListItemTemplate; }, new list::FixedHeightItemArranger ); break; default:; } view = _view; } /*********************************************************************** GuiTextList ***********************************************************************/ GuiTextList::GuiTextList(theme::ThemeName themeName) :GuiVirtualTextList(themeName, new list::TextItemProvider) { items=dynamic_cast(itemProvider.Obj()); items->listControl=this; } GuiTextList::~GuiTextList() { } list::TextItemProvider& GuiTextList::GetItems() { return *items; } Ptr GuiTextList::GetSelectedItem() { vint index = GetSelectedItemIndex(); if (index == -1) return 0; return items->Get(index); } } } } /*********************************************************************** .\CONTROLS\LISTCONTROLPACKAGE\GUITREEVIEWCONTROLS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace elements; using namespace compositions; using namespace reflection::description; namespace tree { const wchar_t* const INodeItemView::Identifier = L"vl::presentation::controls::tree::INodeItemView"; /*********************************************************************** NodeItemProvider ***********************************************************************/ Ptr NodeItemProvider::GetNodeByOffset(Ptr provider, vint offset) { if (offset == 0) return provider; Ptr result; if (provider->GetExpanding() && offset > 0) { offset -= 1; vint count = provider->GetChildCount(); for (vint i = 0; (!result && i < count); i++) { auto child = provider->GetChild(i); vint visibleCount = child->CalculateTotalVisibleNodes(); if (offset < visibleCount) { result = GetNodeByOffset(child, offset); } else { offset -= visibleCount; } } } return result; } void NodeItemProvider::OnAttached(INodeRootProvider* provider) { } void NodeItemProvider::OnBeforeItemModified(INodeProvider* parentNode, vint start, vint count, vint newCount) { vint offset = 0; vint base = CalculateNodeVisibilityIndexInternal(parentNode); if (base != -2 && parentNode->GetExpanding()) { for (vint i = 0; i < count; i++) { auto child = parentNode->GetChild(start + i); offset += child->CalculateTotalVisibleNodes(); } } offsetBeforeChildModifieds.Set(parentNode, offset); } void NodeItemProvider::OnAfterItemModified(INodeProvider* parentNode, vint start, vint count, vint newCount) { vint offsetBeforeChildModified = 0; { vint index = offsetBeforeChildModifieds.Keys().IndexOf(parentNode); if (index != -1) { offsetBeforeChildModified = offsetBeforeChildModifieds.Values().Get(index); offsetBeforeChildModifieds.Remove(parentNode); } } vint base = CalculateNodeVisibilityIndexInternal(parentNode); if (base != -2 && parentNode->GetExpanding()) { vint offset = 0; vint firstChildStart = -1; for (vint i = 0; i < newCount; i++) { auto child = parentNode->GetChild(start + i); if (i == 0) { firstChildStart = CalculateNodeVisibilityIndexInternal(child.Obj()); } offset += child->CalculateTotalVisibleNodes(); } if (firstChildStart == -1) { vint childCount = parentNode->GetChildCount(); if (childCount == 0) { firstChildStart = base + 1; } else if (start < childCount) { auto child = parentNode->GetChild(start); firstChildStart = CalculateNodeVisibilityIndexInternal(child.Obj()); } else { auto child = parentNode->GetChild(start - 1); firstChildStart = CalculateNodeVisibilityIndexInternal(child.Obj()); firstChildStart += child->CalculateTotalVisibleNodes(); } } InvokeOnItemModified(firstChildStart, offsetBeforeChildModified, offset); } } void NodeItemProvider::OnItemExpanded(INodeProvider* node) { vint base = CalculateNodeVisibilityIndexInternal(node); if (base != -2) { vint visibility = node->CalculateTotalVisibleNodes(); InvokeOnItemModified(base + 1, 0, visibility - 1); } } void NodeItemProvider::OnItemCollapsed(INodeProvider* node) { vint base = CalculateNodeVisibilityIndexInternal(node); if (base != -2) { vint visibility = 0; vint count = node->GetChildCount(); for (vint i = 0; i < count; i++) { auto child = node->GetChild(i); visibility += child->CalculateTotalVisibleNodes(); } InvokeOnItemModified(base + 1, visibility, 0); } } vint NodeItemProvider::CalculateNodeVisibilityIndexInternal(INodeProvider* node) { auto parent = node->GetParent(); if (!parent) { return -1; } if (!parent->GetExpanding()) { return -2; } vint index = CalculateNodeVisibilityIndexInternal(parent.Obj()); if (index == -2) { return -2; } vint count = parent->GetChildCount(); for (vint i = 0; i < count; i++) { auto child = parent->GetChild(i); bool findResult = child == node; if (findResult) { index++; } else { index += child->CalculateTotalVisibleNodes(); } if (findResult) { return index; } } return -1; } vint NodeItemProvider::CalculateNodeVisibilityIndex(INodeProvider* node) { vint result = CalculateNodeVisibilityIndexInternal(node); return result < 0 ? -1 : result; } Ptr NodeItemProvider::RequestNode(vint index) { if(root->CanGetNodeByVisibleIndex()) { return root->GetNodeByVisibleIndex(index+1); } else { return GetNodeByOffset(root->GetRootNode(), index+1); } } NodeItemProvider::NodeItemProvider(Ptr _root) :root(_root) { root->AttachCallback(this); } NodeItemProvider::~NodeItemProvider() { root->DetachCallback(this); } Ptr NodeItemProvider::GetRoot() { return root; } vint NodeItemProvider::Count() { return root->GetRootNode()->CalculateTotalVisibleNodes()-1; } WString NodeItemProvider::GetTextValue(vint itemIndex) { if (auto node = RequestNode(itemIndex)) { return root->GetTextValue(node.Obj()); } return L""; } description::Value NodeItemProvider::GetBindingValue(vint itemIndex) { if (auto node = RequestNode(itemIndex)) { return root->GetBindingValue(node.Obj()); } return Value(); } IDescriptable* NodeItemProvider::RequestView(const WString& identifier) { if(identifier==INodeItemView::Identifier) { return (INodeItemView*)this; } else { return root->RequestView(identifier); } } /*********************************************************************** MemoryNodeProvider::NodeCollection ***********************************************************************/ void MemoryNodeProvider::NodeCollection::OnBeforeChildModified(vint start, vint count, vint newCount) { if (ownerProvider->expanding) { for (vint i = 0; i < count; i++) { offsetBeforeChildModified += items[start + i]->totalVisibleNodeCount; } } INodeProviderCallback* proxy = ownerProvider->GetCallbackProxyInternal(); if (proxy) { proxy->OnBeforeItemModified(ownerProvider, start, count, newCount); } } void MemoryNodeProvider::NodeCollection::OnAfterChildModified(vint start, vint count, vint newCount) { ownerProvider->childCount += (newCount - count); if (ownerProvider->expanding) { vint offset = 0; for (vint i = 0; i < newCount; i++) { offset += items[start + i]->totalVisibleNodeCount; } ownerProvider->OnChildTotalVisibleNodesChanged(offset - offsetBeforeChildModified); } offsetBeforeChildModified = 0; INodeProviderCallback* proxy = ownerProvider->GetCallbackProxyInternal(); if (proxy) { proxy->OnAfterItemModified(ownerProvider, start, count, newCount); } } bool MemoryNodeProvider::NodeCollection::QueryInsert(vint index, Ptr const& child) { return child->parent == 0; } bool MemoryNodeProvider::NodeCollection::QueryRemove(vint index, Ptr const& child) { return child->parent == ownerProvider; } void MemoryNodeProvider::NodeCollection::BeforeInsert(vint index, Ptr const& child) { OnBeforeChildModified(index, 0, 1); child->parent = ownerProvider; } void MemoryNodeProvider::NodeCollection::BeforeRemove(vint index, Ptr const& child) { OnBeforeChildModified(index, 1, 0); child->parent = 0; } void MemoryNodeProvider::NodeCollection::AfterInsert(vint index, Ptr const& child) { OnAfterChildModified(index, 0, 1); } void MemoryNodeProvider::NodeCollection::AfterRemove(vint index, vint count) { OnAfterChildModified(index, count, 0); } MemoryNodeProvider::NodeCollection::NodeCollection() :ownerProvider(0) { } /*********************************************************************** MemoryNodeProvider ***********************************************************************/ INodeProviderCallback* MemoryNodeProvider::GetCallbackProxyInternal() { if(parent) { return parent->GetCallbackProxyInternal(); } else { return 0; } } void MemoryNodeProvider::OnChildTotalVisibleNodesChanged(vint offset) { totalVisibleNodeCount+=offset; if(parent) { parent->OnChildTotalVisibleNodesChanged(offset); } } MemoryNodeProvider::MemoryNodeProvider(Ptr _data) :data(_data) { children.ownerProvider=this; } MemoryNodeProvider::~MemoryNodeProvider() { } Ptr MemoryNodeProvider::GetData() { return data; } void MemoryNodeProvider::SetData(const Ptr& value) { data=value; NotifyDataModified(); } void MemoryNodeProvider::NotifyDataModified() { if(parent) { vint index=parent->children.IndexOf(this); INodeProviderCallback* proxy=GetCallbackProxyInternal(); if(proxy) { proxy->OnBeforeItemModified(parent, index, 1, 1); proxy->OnAfterItemModified(parent, index, 1, 1); } } } MemoryNodeProvider::NodeCollection& MemoryNodeProvider::Children() { return children; } bool MemoryNodeProvider::GetExpanding() { return expanding; } void MemoryNodeProvider::SetExpanding(bool value) { if(expanding!=value) { expanding=value; vint offset=0; for(vint i=0;itotalVisibleNodeCount; } OnChildTotalVisibleNodesChanged(expanding?offset:-offset); INodeProviderCallback* proxy=GetCallbackProxyInternal(); if(proxy) { if(expanding) { proxy->OnItemExpanded(this); } else { proxy->OnItemCollapsed(this); } } } } vint MemoryNodeProvider::CalculateTotalVisibleNodes() { return totalVisibleNodeCount; } vint MemoryNodeProvider::GetChildCount() { return childCount; } Ptr MemoryNodeProvider::GetParent() { return parent; } Ptr MemoryNodeProvider::GetChild(vint index) { if(0<=index && indexOnBeforeItemModified(parentNode, start, count, newCount); } } void NodeRootProviderBase::OnAfterItemModified(INodeProvider* parentNode, vint start, vint count, vint newCount) { for(vint i=0;iOnAfterItemModified(parentNode, start, count, newCount); } } void NodeRootProviderBase::OnItemExpanded(INodeProvider* node) { for(vint i=0;iOnItemExpanded(node); } } void NodeRootProviderBase::OnItemCollapsed(INodeProvider* node) { for(vint i=0;iOnItemCollapsed(node); } } NodeRootProviderBase::NodeRootProviderBase() { } NodeRootProviderBase::~NodeRootProviderBase() { } bool NodeRootProviderBase::CanGetNodeByVisibleIndex() { return false; } Ptr NodeRootProviderBase::GetNodeByVisibleIndex(vint index) { return nullptr; } bool NodeRootProviderBase::AttachCallback(INodeProviderCallback* value) { if(callbacks.Contains(value)) { return false; } else { callbacks.Add(value); value->OnAttached(this); return true; } } bool NodeRootProviderBase::DetachCallback(INodeProviderCallback* value) { vint index=callbacks.IndexOf(value); if(index==-1) { return false; } else { value->OnAttached(0); callbacks.Remove(value); return true; } } IDescriptable* NodeRootProviderBase::RequestView(const WString& identifier) { return 0; } /*********************************************************************** MemoryNodeRootProvider ***********************************************************************/ INodeProviderCallback* MemoryNodeRootProvider::GetCallbackProxyInternal() { return this; } MemoryNodeRootProvider::MemoryNodeRootProvider() { SetExpanding(true); } MemoryNodeRootProvider::~MemoryNodeRootProvider() { } Ptr MemoryNodeRootProvider::GetRootNode() { return this; } MemoryNodeProvider* MemoryNodeRootProvider::GetMemoryNode(INodeProvider* node) { return dynamic_cast(node); } } /*********************************************************************** GuiVirtualTreeListControl ***********************************************************************/ void GuiVirtualTreeListControl::BeforeControlTemplateUninstalled_() { } void GuiVirtualTreeListControl::AfterControlTemplateInstalled_(bool initialize) { } void GuiVirtualTreeListControl::OnAttached(tree::INodeRootProvider* provider) { } void GuiVirtualTreeListControl::OnBeforeItemModified(tree::INodeProvider* parentNode, vint start, vint count, vint newCount) { } void GuiVirtualTreeListControl::OnAfterItemModified(tree::INodeProvider* parentNode, vint start, vint count, vint newCount) { } void GuiVirtualTreeListControl::OnItemExpanded(tree::INodeProvider* node) { GuiNodeEventArgs arguments; (GuiEventArgs&)arguments=GetNotifyEventArguments(); arguments.node=node; NodeExpanded.Execute(arguments); } void GuiVirtualTreeListControl::OnItemCollapsed(tree::INodeProvider* node) { GuiNodeEventArgs arguments; (GuiEventArgs&)arguments=GetNotifyEventArguments(); arguments.node=node; NodeCollapsed.Execute(arguments); } void GuiVirtualTreeListControl::OnItemMouseEvent(compositions::GuiNodeMouseEvent& nodeEvent, compositions::GuiGraphicsComposition* sender, compositions::GuiItemMouseEventArgs& arguments) { auto node = GetNodeItemView()->RequestNode(arguments.itemIndex); if (node) { GuiNodeMouseEventArgs redirectArguments; (GuiMouseEventArgs&)redirectArguments = arguments; redirectArguments.node = node.Obj(); nodeEvent.Execute(redirectArguments); (GuiMouseEventArgs&)arguments = redirectArguments; } } void GuiVirtualTreeListControl::OnItemNotifyEvent(compositions::GuiNodeNotifyEvent& nodeEvent, compositions::GuiGraphicsComposition* sender, compositions::GuiItemEventArgs& arguments) { if (auto node = GetNodeItemView()->RequestNode(arguments.itemIndex)) { GuiNodeEventArgs redirectArguments; (GuiEventArgs&)redirectArguments = arguments; redirectArguments.node = node.Obj(); nodeEvent.Execute(redirectArguments); (GuiEventArgs&)arguments = redirectArguments; } } #define ATTACH_ITEM_MOUSE_EVENT(NODEEVENTNAME, ITEMEVENTNAME)\ {\ Func func(this, &GuiVirtualTreeListControl::OnItemMouseEvent);\ ITEMEVENTNAME.AttachFunction(Curry(func)(NODEEVENTNAME));\ }\ #define ATTACH_ITEM_NOTIFY_EVENT(NODEEVENTNAME, ITEMEVENTNAME)\ {\ Func func(this, &GuiVirtualTreeListControl::OnItemNotifyEvent);\ ITEMEVENTNAME.AttachFunction(Curry(func)(NODEEVENTNAME));\ }\ void GuiVirtualTreeListControl::OnNodeLeftButtonDoubleClick(compositions::GuiGraphicsComposition* sender, compositions::GuiNodeMouseEventArgs& arguments) { if (arguments.node->GetChildCount() > 0) { arguments.node->SetExpanding(!arguments.node->GetExpanding()); } } GuiVirtualTreeListControl::GuiVirtualTreeListControl(theme::ThemeName themeName, Ptr _nodeRootProvider) :GuiSelectableListControl(themeName, new tree::NodeItemProvider(_nodeRootProvider)) { nodeItemProvider = dynamic_cast(GetItemProvider()); nodeItemView = dynamic_cast(GetItemProvider()->RequestView(tree::INodeItemView::Identifier)); NodeLeftButtonDown.SetAssociatedComposition(boundsComposition); NodeLeftButtonUp.SetAssociatedComposition(boundsComposition); NodeLeftButtonDoubleClick.SetAssociatedComposition(boundsComposition); NodeMiddleButtonDown.SetAssociatedComposition(boundsComposition); NodeMiddleButtonUp.SetAssociatedComposition(boundsComposition); NodeMiddleButtonDoubleClick.SetAssociatedComposition(boundsComposition); NodeRightButtonDown.SetAssociatedComposition(boundsComposition); NodeRightButtonUp.SetAssociatedComposition(boundsComposition); NodeRightButtonDoubleClick.SetAssociatedComposition(boundsComposition); NodeMouseMove.SetAssociatedComposition(boundsComposition); NodeMouseEnter.SetAssociatedComposition(boundsComposition); NodeMouseLeave.SetAssociatedComposition(boundsComposition); NodeExpanded.SetAssociatedComposition(boundsComposition); NodeCollapsed.SetAssociatedComposition(boundsComposition); ATTACH_ITEM_MOUSE_EVENT(NodeLeftButtonDown, ItemLeftButtonDown); ATTACH_ITEM_MOUSE_EVENT(NodeLeftButtonUp, ItemLeftButtonUp); ATTACH_ITEM_MOUSE_EVENT(NodeLeftButtonDoubleClick, ItemLeftButtonDoubleClick); ATTACH_ITEM_MOUSE_EVENT(NodeMiddleButtonDown, ItemMiddleButtonDown); ATTACH_ITEM_MOUSE_EVENT(NodeMiddleButtonUp, ItemMiddleButtonUp); ATTACH_ITEM_MOUSE_EVENT(NodeMiddleButtonDoubleClick, ItemMiddleButtonDoubleClick); ATTACH_ITEM_MOUSE_EVENT(NodeRightButtonDown, ItemRightButtonDown); ATTACH_ITEM_MOUSE_EVENT(NodeRightButtonUp, ItemRightButtonUp); ATTACH_ITEM_MOUSE_EVENT(NodeRightButtonDoubleClick, ItemRightButtonDoubleClick); ATTACH_ITEM_MOUSE_EVENT(NodeMouseMove, ItemMouseMove); ATTACH_ITEM_NOTIFY_EVENT(NodeMouseEnter, ItemMouseEnter); ATTACH_ITEM_NOTIFY_EVENT(NodeMouseLeave, ItemMouseLeave); nodeItemProvider->GetRoot()->AttachCallback(this); NodeLeftButtonDoubleClick.AttachMethod(this, &GuiVirtualTreeListControl::OnNodeLeftButtonDoubleClick); } #undef ATTACH_ITEM_MOUSE_EVENT #undef ATTACH_ITEM_NOTIFY_EVENT GuiVirtualTreeListControl::~GuiVirtualTreeListControl() { } tree::INodeItemView* GuiVirtualTreeListControl::GetNodeItemView() { return nodeItemView; } tree::INodeRootProvider* GuiVirtualTreeListControl::GetNodeRootProvider() { return nodeItemProvider->GetRoot().Obj(); } namespace tree { /*********************************************************************** TreeViewItem ***********************************************************************/ const wchar_t* const ITreeViewItemView::Identifier = L"vl::presentation::controls::tree::ITreeViewItemView"; TreeViewItem::TreeViewItem() { } TreeViewItem::TreeViewItem(const Ptr& _image, const WString& _text) :image(_image) ,text(_text) { } /*********************************************************************** TreeViewItemRootProvider ***********************************************************************/ Ptr TreeViewItemRootProvider::GetNodeImage(INodeProvider* node) { MemoryNodeProvider* memoryNode=dynamic_cast(node); if(memoryNode) { Ptr data=memoryNode->GetData().Cast(); if(data) { return data->image; } } return 0; } WString TreeViewItemRootProvider::GetTextValue(INodeProvider* node) { MemoryNodeProvider* memoryNode = dynamic_cast(node); if (memoryNode) { Ptr data = memoryNode->GetData().Cast(); if (data) { return data->text; } } return L""; } description::Value TreeViewItemRootProvider::GetBindingValue(INodeProvider* node) { return Value::From(GetTreeViewData(node)); } TreeViewItemRootProvider::TreeViewItemRootProvider() { } TreeViewItemRootProvider::~TreeViewItemRootProvider() { } IDescriptable* TreeViewItemRootProvider::RequestView(const WString& identifier) { if(identifier==ITreeViewItemView::Identifier) { return (ITreeViewItemView*)this; } else { return MemoryNodeRootProvider::RequestView(identifier); } } Ptr TreeViewItemRootProvider::GetTreeViewData(INodeProvider* node) { MemoryNodeProvider* memoryNode=GetMemoryNode(node); if(memoryNode) { return memoryNode->GetData().Cast(); } else { return 0; } } void TreeViewItemRootProvider::SetTreeViewData(INodeProvider* node, Ptr value) { MemoryNodeProvider* memoryNode=GetMemoryNode(node); if(memoryNode) { memoryNode->SetData(value); } } void TreeViewItemRootProvider::UpdateTreeViewData(INodeProvider* node) { MemoryNodeProvider* memoryNode=GetMemoryNode(node); if(memoryNode) { memoryNode->NotifyDataModified(); } } } /*********************************************************************** GuiVirtualTreeView ***********************************************************************/ templates::GuiTreeItemTemplate* GuiVirtualTreeView::GetStyleFromNode(tree::INodeProvider* node) { if (itemArranger) { vint index = nodeItemView->CalculateNodeVisibilityIndex(node); if (index != -1) { auto style = itemArranger->GetVisibleStyle(index); return dynamic_cast(style); } } return nullptr; } void GuiVirtualTreeView::SetStyleExpanding(tree::INodeProvider* node, bool expanding) { if (auto treeItemStyle = GetStyleFromNode(node)) { treeItemStyle->SetExpanding(expanding); } } void GuiVirtualTreeView::SetStyleExpandable(tree::INodeProvider* node, bool expandable) { if (auto treeItemStyle = GetStyleFromNode(node)) { treeItemStyle->SetExpandable(expandable); } } void GuiVirtualTreeView::OnAfterItemModified(tree::INodeProvider* parentNode, vint start, vint count, vint newCount) { GuiVirtualTreeListControl::OnAfterItemModified(parentNode, start, count, newCount); SetStyleExpandable(parentNode, parentNode->GetChildCount() > 0); } void GuiVirtualTreeView::OnItemExpanded(tree::INodeProvider* node) { GuiVirtualTreeListControl::OnItemExpanded(node); SetStyleExpanding(node, true); } void GuiVirtualTreeView::OnItemCollapsed(tree::INodeProvider* node) { GuiVirtualTreeListControl::OnItemCollapsed(node); SetStyleExpanding(node, false); } void GuiVirtualTreeView::OnStyleInstalled(vint itemIndex, ItemStyle* style) { GuiVirtualTreeListControl::OnStyleInstalled(itemIndex, style); if (auto treeItemStyle = dynamic_cast(style)) { treeItemStyle->SetTextColor(GetControlTemplateObject(true)->GetTextColor()); if (treeViewItemView) { if (auto node = nodeItemView->RequestNode(itemIndex)) { treeItemStyle->SetImage(treeViewItemView->GetNodeImage(node.Obj())); treeItemStyle->SetExpanding(node->GetExpanding()); treeItemStyle->SetExpandable(node->GetChildCount() > 0); { vint level = -1; auto current = node; while (current->GetParent()) { level++; current = current->GetParent(); } treeItemStyle->SetLevel(level); } } } } } GuiVirtualTreeView::GuiVirtualTreeView(theme::ThemeName themeName, Ptr _nodeRootProvider) :GuiVirtualTreeListControl(themeName, _nodeRootProvider) { treeViewItemView = dynamic_cast(GetNodeRootProvider()->RequestView(tree::ITreeViewItemView::Identifier)); SetStyleAndArranger( [](const Value&) { return new tree::DefaultTreeItemTemplate; }, new list::FixedHeightItemArranger ); } GuiVirtualTreeView::~GuiVirtualTreeView() { } /*********************************************************************** GuiTreeView ***********************************************************************/ GuiTreeView::GuiTreeView(theme::ThemeName themeName) :GuiVirtualTreeView(themeName, new tree::TreeViewItemRootProvider) { nodes = nodeItemProvider->GetRoot().Cast(); } GuiTreeView::~GuiTreeView() { } Ptr GuiTreeView::Nodes() { return nodes; } Ptr GuiTreeView::GetSelectedItem() { Ptr result; vint index = GetSelectedItemIndex(); if (index != -1) { if (auto node = nodeItemView->RequestNode(index)) { if (auto memoryNode = node.Cast()) { result = memoryNode->GetData().Cast(); } } } return result; } namespace tree { /*********************************************************************** DefaultTreeItemTemplate ***********************************************************************/ void DefaultTreeItemTemplate::OnInitialize() { templates::GuiTreeItemTemplate::OnInitialize(); SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); table = new GuiTableComposition; AddChild(table); table->SetRowsAndColumns(3, 4); table->SetRowOption(0, GuiCellOption::PercentageOption(0.5)); table->SetRowOption(1, GuiCellOption::MinSizeOption()); table->SetRowOption(2, GuiCellOption::PercentageOption(0.5)); table->SetColumnOption(0, GuiCellOption::AbsoluteOption(0)); table->SetColumnOption(1, GuiCellOption::MinSizeOption()); table->SetColumnOption(2, GuiCellOption::MinSizeOption()); table->SetColumnOption(3, GuiCellOption::MinSizeOption()); table->SetAlignmentToParent(Margin(0, 0, 0, 0)); table->SetCellPadding(2); { GuiCellComposition* cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(0, 1, 3, 1); cell->SetPreferredMinSize(Size(16, 16)); expandingButton = new GuiSelectableButton(theme::ThemeName::TreeItemExpander); if (auto treeView = dynamic_cast(listControl)) { if (auto expanderStyle = treeView->GetControlTemplateObject(true)->GetExpandingDecoratorTemplate()) { expandingButton->SetControlTemplate(expanderStyle); } } expandingButton->SetAutoSelection(false); expandingButton->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); expandingButton->GetBoundsComposition()->GetEventReceiver()->leftButtonDoubleClick.AttachMethod(this, &DefaultTreeItemTemplate::OnExpandingButtonDoubleClick); expandingButton->Clicked.AttachMethod(this, &DefaultTreeItemTemplate::OnExpandingButtonClicked); cell->AddChild(expandingButton->GetBoundsComposition()); } { GuiCellComposition* cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(1, 2, 1, 1); cell->SetPreferredMinSize(Size(16, 16)); imageElement = GuiImageFrameElement::Create(); imageElement->SetStretch(true); cell->SetOwnedElement(imageElement); } { GuiCellComposition* cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(0, 3, 3, 1); cell->SetPreferredMinSize(Size(192, 0)); textElement = GuiSolidLabelElement::Create(); textElement->SetAlignments(Alignment::Left, Alignment::Center); textElement->SetEllipse(true); cell->SetOwnedElement(textElement); } FontChanged.AttachMethod(this, &DefaultTreeItemTemplate::OnFontChanged); TextChanged.AttachMethod(this, &DefaultTreeItemTemplate::OnTextChanged); TextColorChanged.AttachMethod(this, &DefaultTreeItemTemplate::OnTextColorChanged); ExpandingChanged.AttachMethod(this, &DefaultTreeItemTemplate::OnExpandingChanged); ExpandableChanged.AttachMethod(this, &DefaultTreeItemTemplate::OnExpandableChanged); LevelChanged.AttachMethod(this, &DefaultTreeItemTemplate::OnLevelChanged); ImageChanged.AttachMethod(this, &DefaultTreeItemTemplate::OnImageChanged); FontChanged.Execute(compositions::GuiEventArgs(this)); TextChanged.Execute(compositions::GuiEventArgs(this)); TextColorChanged.Execute(compositions::GuiEventArgs(this)); ExpandingChanged.Execute(compositions::GuiEventArgs(this)); ExpandableChanged.Execute(compositions::GuiEventArgs(this)); LevelChanged.Execute(compositions::GuiEventArgs(this)); ImageChanged.Execute(compositions::GuiEventArgs(this)); } void DefaultTreeItemTemplate::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { textElement->SetFont(GetFont()); } void DefaultTreeItemTemplate::OnTextChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { textElement->SetText(GetText()); } void DefaultTreeItemTemplate::OnTextColorChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { textElement->SetColor(GetTextColor()); } void DefaultTreeItemTemplate::OnExpandingChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { expandingButton->SetSelected(GetExpanding()); } void DefaultTreeItemTemplate::OnExpandableChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { expandingButton->SetVisible(GetExpandable()); } void DefaultTreeItemTemplate::OnLevelChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { table->SetColumnOption(0, GuiCellOption::AbsoluteOption(GetLevel() * 12)); } void DefaultTreeItemTemplate::OnImageChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if (auto imageData = GetImage()) { imageElement->SetImage(imageData->GetImage(), imageData->GetFrameIndex()); } else { imageElement->SetImage(nullptr); } } void DefaultTreeItemTemplate::OnExpandingButtonDoubleClick(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { arguments.handled = true; } void DefaultTreeItemTemplate::OnExpandingButtonClicked(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if (expandingButton->GetVisuallyEnabled()) { if (auto treeControl = dynamic_cast(listControl)) { if (auto view = treeControl->GetNodeItemView()) { vint index = treeControl->GetArranger()->GetVisibleIndex(this); if (index != -1) { if (auto node = view->RequestNode(index)) { bool expanding = node->GetExpanding(); node->SetExpanding(!expanding); } } } } } } DefaultTreeItemTemplate::DefaultTreeItemTemplate() { } DefaultTreeItemTemplate::~DefaultTreeItemTemplate() { } } } } } /*********************************************************************** .\CONTROLS\GUIDATETIMECONTROLS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace collections; using namespace compositions; using namespace elements; /*********************************************************************** GuiDatePicker::CommandExecutor ***********************************************************************/ GuiDatePicker::CommandExecutor::CommandExecutor(GuiDatePicker* _datePicker) :datePicker(_datePicker) { } GuiDatePicker::CommandExecutor::~CommandExecutor() { } void GuiDatePicker::CommandExecutor::NotifyDateChanged() { datePicker->date = datePicker->GetControlTemplateObject(true)->GetDate(); datePicker->UpdateText(); datePicker->DateChanged.Execute(datePicker->GetNotifyEventArguments()); } void GuiDatePicker::CommandExecutor::NotifyDateNavigated() { datePicker->DateNavigated.Execute(datePicker->GetNotifyEventArguments()); } void GuiDatePicker::CommandExecutor::NotifyDateSelected() { datePicker->DateSelected.Execute(datePicker->GetNotifyEventArguments()); } /*********************************************************************** GuiDatePicker ***********************************************************************/ void GuiDatePicker::BeforeControlTemplateUninstalled_() { auto ct = GetControlTemplateObject(false); if (!ct) return; ct->SetCommands(nullptr); } void GuiDatePicker::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); ct->SetCommands(commandExecutor.Obj()); ct->SetDate(date); ct->SetDateLocale(dateLocale); UpdateText(); } void GuiDatePicker::UpdateText() { GuiControl::SetText(dateLocale.FormatDate(dateFormat, date)); } GuiDatePicker::GuiDatePicker(theme::ThemeName themeName) :GuiControl(themeName) { commandExecutor = new CommandExecutor(this); SetDateLocale(Locale::UserDefault()); SetDate(DateTime::LocalTime()); DateChanged.SetAssociatedComposition(boundsComposition); DateNavigated.SetAssociatedComposition(boundsComposition); DateSelected.SetAssociatedComposition(boundsComposition); DateFormatChanged.SetAssociatedComposition(boundsComposition); DateLocaleChanged.SetAssociatedComposition(boundsComposition); commandExecutor->NotifyDateChanged(); } GuiDatePicker::~GuiDatePicker() { } const DateTime& GuiDatePicker::GetDate() { return date; } void GuiDatePicker::SetDate(const DateTime& value) { if (date != value) { date = value; GetControlTemplateObject(true)->SetDate(value); } } const WString& GuiDatePicker::GetDateFormat() { return dateFormat; } void GuiDatePicker::SetDateFormat(const WString& value) { dateFormat=value; UpdateText(); DateFormatChanged.Execute(GetNotifyEventArguments()); } const Locale& GuiDatePicker::GetDateLocale() { return dateLocale; } void GuiDatePicker::SetDateLocale(const Locale& value) { dateLocale=value; List formats; dateLocale.GetLongDateFormats(formats); if(formats.Count()>0) { dateFormat=formats[0]; } GetControlTemplateObject(true)->SetDateLocale(dateLocale); UpdateText(); DateFormatChanged.Execute(GetNotifyEventArguments()); DateLocaleChanged.Execute(GetNotifyEventArguments()); } void GuiDatePicker::SetText(const WString& value) { } /*********************************************************************** GuiDateComboBox ***********************************************************************/ void GuiDateComboBox::UpdateText() { SetText(datePicker->GetDateLocale().FormatDate(datePicker->GetDateFormat(), selectedDate)); } void GuiDateComboBox::NotifyUpdateSelectedDate() { UpdateText(); SelectedDateChanged.Execute(GetNotifyEventArguments()); } void GuiDateComboBox::OnSubMenuOpeningChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { datePicker->SetDate(selectedDate); } void GuiDateComboBox::datePicker_DateLocaleChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { UpdateText(); } void GuiDateComboBox::datePicker_DateFormatChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { UpdateText(); } void GuiDateComboBox::datePicker_DateSelected(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { selectedDate=datePicker->GetDate(); GetSubMenu()->Hide(); SelectItem(); NotifyUpdateSelectedDate(); } GuiDateComboBox::GuiDateComboBox(theme::ThemeName themeName, GuiDatePicker* _datePicker) :GuiComboBoxBase(themeName) ,datePicker(_datePicker) { SelectedDateChanged.SetAssociatedComposition(GetBoundsComposition()); datePicker->DateSelected.AttachMethod(this, &GuiDateComboBox::datePicker_DateSelected); datePicker->DateLocaleChanged.AttachMethod(this, &GuiDateComboBox::datePicker_DateLocaleChanged); datePicker->DateFormatChanged.AttachMethod(this, &GuiDateComboBox::datePicker_DateFormatChanged); datePicker->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); GetSubMenu()->GetContainerComposition()->AddChild(datePicker->GetBoundsComposition()); selectedDate=datePicker->GetDate(); SubMenuOpeningChanged.AttachMethod(this, &GuiDateComboBox::OnSubMenuOpeningChanged); SetFont(GetFont()); SetText(datePicker->GetText()); } GuiDateComboBox::~GuiDateComboBox() { } void GuiDateComboBox::SetFont(const FontProperties& value) { GuiComboBoxBase::SetFont(value); datePicker->SetFont(value); } const DateTime& GuiDateComboBox::GetSelectedDate() { return selectedDate; } void GuiDateComboBox::SetSelectedDate(const DateTime& value) { selectedDate=value; NotifyUpdateSelectedDate(); } GuiDatePicker* GuiDateComboBox::GetDatePicker() { return datePicker; } } } } /*********************************************************************** .\CONTROLS\LISTCONTROLPACKAGE\GUICOMBOCONTROLS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { /*********************************************************************** GuiComboBoxBase::CommandExecutor ***********************************************************************/ GuiComboBoxBase::CommandExecutor::CommandExecutor(GuiComboBoxBase* _combo) :combo(_combo) { } GuiComboBoxBase::CommandExecutor::~CommandExecutor() { } void GuiComboBoxBase::CommandExecutor::SelectItem() { combo->SelectItem(); } /*********************************************************************** GuiComboBoxBase ***********************************************************************/ void GuiComboBoxBase::BeforeControlTemplateUninstalled_() { auto ct = GetControlTemplateObject(false); if (!ct) return; ct->SetCommands(nullptr); } void GuiComboBoxBase::AfterControlTemplateInstalled_(bool initialize) { GetControlTemplateObject(true)->SetCommands(commandExecutor.Obj()); } bool GuiComboBoxBase::IsAltAvailable() { return false; } IGuiMenuService::Direction GuiComboBoxBase::GetSubMenuDirection() { return IGuiMenuService::Horizontal; } void GuiComboBoxBase::SelectItem() { ItemSelected.Execute(GetNotifyEventArguments()); } void GuiComboBoxBase::OnBoundsChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { Size size=GetPreferredMenuClientSize(); size.x=boundsComposition->GetBounds().Width(); SetPreferredMenuClientSize(size); } GuiComboBoxBase::GuiComboBoxBase(theme::ThemeName themeName) :GuiMenuButton(themeName) { commandExecutor = new CommandExecutor(this); CreateSubMenu(); SetCascadeAction(false); boundsComposition->BoundsChanged.AttachMethod(this, &GuiComboBoxBase::OnBoundsChanged); } GuiComboBoxBase::~GuiComboBoxBase() { } /*********************************************************************** GuiComboBoxListControl ***********************************************************************/ void GuiComboBoxListControl::BeforeControlTemplateUninstalled() { GuiComboBoxBase::BeforeControlTemplateUninstalled(); } void GuiComboBoxListControl::AfterControlTemplateInstalled(bool initialize) { GuiComboBoxBase::AfterControlTemplateInstalled(initialize); GetControlTemplateObject(true)->SetTextVisible(!itemStyleProperty); } bool GuiComboBoxListControl::IsAltAvailable() { return true; } void GuiComboBoxListControl::OnActiveAlt() { GuiMenuButton::OnActiveAlt(); GetSubMenu()->GetNativeWindow()->SetFocus(); containedListControl->SetFocus(); } void GuiComboBoxListControl::RemoveStyleController() { if (itemStyleController) { SafeDeleteComposition(itemStyleController); itemStyleController = nullptr; } } void GuiComboBoxListControl::InstallStyleController(vint itemIndex) { if (itemStyleProperty) { if (itemIndex != -1) { auto item = containedListControl->GetItemProvider()->GetBindingValue(itemIndex); if (!item.IsNull()) { if (auto style = itemStyleProperty(item)) { itemStyleController = style; itemStyleController->SetText(GetText()); itemStyleController->SetFont(GetFont()); itemStyleController->SetContext(GetContext()); itemStyleController->SetVisuallyEnabled(GetVisuallyEnabled()); itemStyleController->SetAlignmentToParent(Margin(0, 0, 0, 0)); containerComposition->AddChild(itemStyleController); } } } } } void GuiComboBoxListControl::DisplaySelectedContent(vint itemIndex) { if(itemIndex==-1) { SetText(L""); } else { WString text = containedListControl->GetItemProvider()->GetTextValue(itemIndex); SetText(text); GetSubMenu()->Hide(); } RemoveStyleController(); InstallStyleController(itemIndex); } void GuiComboBoxListControl::AdoptSubMenuSize() { Size expectedSize(0, GetFont().size * 20); Size adoptedSize = containedListControl->GetAdoptedSize(expectedSize); Size clientSize = GetPreferredMenuClientSize(); clientSize.y = adoptedSize.y + GetSubMenu()->GetClientSize().y - containedListControl->GetBoundsComposition()->GetBounds().Height(); SetPreferredMenuClientSize(clientSize); if (GetSubMenuOpening()) { GetSubMenu()->SetClientSize(clientSize); } } void GuiComboBoxListControl::OnTextChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if (itemStyleController) { itemStyleController->SetText(GetText()); } } void GuiComboBoxListControl::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if (itemStyleController) { itemStyleController->SetFont(GetFont()); } AdoptSubMenuSize(); } void GuiComboBoxListControl::OnContextChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if (itemStyleController) { itemStyleController->SetContext(GetContext()); } AdoptSubMenuSize(); } void GuiComboBoxListControl::OnVisuallyEnabledChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if (itemStyleController) { itemStyleController->SetVisuallyEnabled(GetVisuallyEnabled()); } } void GuiComboBoxListControl::OnListControlAdoptedSizeInvalidated(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { AdoptSubMenuSize(); } void GuiComboBoxListControl::OnListControlBoundsChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { GetApplication()->InvokeLambdaInMainThread(GetRelatedControlHost(), [=]() { AdoptSubMenuSize(); }); } void GuiComboBoxListControl::OnListControlSelectionChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { DisplaySelectedContent(GetSelectedIndex()); SelectItem(); SelectedIndexChanged.Execute(GetNotifyEventArguments()); } GuiComboBoxListControl::GuiComboBoxListControl(theme::ThemeName themeName, GuiSelectableListControl* _containedListControl) :GuiComboBoxBase(themeName) , containedListControl(_containedListControl) { TextChanged.AttachMethod(this, &GuiComboBoxListControl::OnTextChanged); FontChanged.AttachMethod(this, &GuiComboBoxListControl::OnFontChanged); ContextChanged.AttachMethod(this, &GuiComboBoxListControl::OnContextChanged); VisuallyEnabledChanged.AttachMethod(this, &GuiComboBoxListControl::OnVisuallyEnabledChanged); containedListControl->SetMultiSelect(false); containedListControl->AdoptedSizeInvalidated.AttachMethod(this, &GuiComboBoxListControl::OnListControlAdoptedSizeInvalidated); containedListControl->SelectionChanged.AttachMethod(this, &GuiComboBoxListControl::OnListControlSelectionChanged); boundsChangedHandler = containedListControl->GetBoundsComposition()->BoundsChanged.AttachMethod(this, &GuiComboBoxListControl::OnListControlBoundsChanged); auto itemProvider = containedListControl->GetItemProvider(); SelectedIndexChanged.SetAssociatedComposition(boundsComposition); containedListControl->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); GetSubMenu()->GetContainerComposition()->AddChild(containedListControl->GetBoundsComposition()); SetFont(GetFont()); } GuiComboBoxListControl::~GuiComboBoxListControl() { containedListControl->GetBoundsComposition()->BoundsChanged.Detach(boundsChangedHandler); boundsChangedHandler = nullptr; } GuiSelectableListControl* GuiComboBoxListControl::GetContainedListControl() { return containedListControl; } GuiComboBoxListControl::ItemStyleProperty GuiComboBoxListControl::GetItemTemplate() { return itemStyleProperty; } void GuiComboBoxListControl::SetItemTemplate(ItemStyleProperty value) { RemoveStyleController(); itemStyleProperty = value; GetControlTemplateObject(true)->SetTextVisible(!itemStyleProperty); InstallStyleController(GetSelectedIndex()); ItemTemplateChanged.Execute(GetNotifyEventArguments()); } vint GuiComboBoxListControl::GetSelectedIndex() { if(containedListControl->GetSelectedItems().Count()==1) { return containedListControl->GetSelectedItems()[0]; } else { return -1; } } void GuiComboBoxListControl::SetSelectedIndex(vint value) { containedListControl->SetSelected(value, true); } description::Value GuiComboBoxListControl::GetSelectedItem() { auto selectedIndex = GetSelectedIndex(); if (selectedIndex != -1) { return containedListControl->GetItemProvider()->GetBindingValue(selectedIndex); } return description::Value(); } GuiListControl::IItemProvider* GuiComboBoxListControl::GetItemProvider() { return containedListControl->GetItemProvider(); } } } } /*********************************************************************** .\CONTROLS\TEMPLATES\GUICOMMONTEMPLATES.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace templates { using namespace elements; using namespace compositions; using namespace templates; using namespace controls; using namespace theme; /*********************************************************************** GuiCommonDatePickerLook ***********************************************************************/ vint GetDayCountForMonth(vint year, vint month) { bool isLeapYear = (year % 100 == 0) ? (year % 400 == 0) : (year % 4 == 0); switch (month) { case 1:case 3:case 5:case 7:case 8:case 10:case 12: return 31; case 4:case 6:case 9:case 11: return 30; default: return isLeapYear ? 29 : 28; } } void StepPreviousMonth(vint& year, vint& month) { if (month == 1) { year--; month = 12; } else { month--; } } void StepNextMonth(vint& year, vint& month) { if (month == 12) { year++; month = 1; } else { month++; } } void GuiCommonDatePickerLook::SetDay(const DateTime& day, vint& index, bool currentMonth) { dateDays[index] = day; GuiSolidLabelElement* label = labelDays[index]; label->SetText(itow(day.day)); label->SetColor(currentMonth ? primaryTextColor : secondaryTextColor); index++; } void GuiCommonDatePickerLook::comboYearMonth_SelectedIndexChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if (!preventComboEvent) { if (comboYear->GetSelectedIndex() != -1 && comboMonth->GetSelectedIndex() != -1) { vint year = comboYear->GetSelectedIndex() + YearFirst; vint month = comboMonth->GetSelectedIndex() + 1; SetDate(DateTime::FromDateTime(year, month, 1)); GuiEventArgs arguments(this); DateChanged.Execute(arguments); commands->NotifyDateChanged(); commands->NotifyDateNavigated(); } } } void GuiCommonDatePickerLook::buttonDay_SelectedChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if (!preventButtonEvent) { GuiSelectableButton* button = dynamic_cast(sender->GetRelatedControl()); if (button->GetSelected()) { vint index = buttonDays.IndexOf(button); if (index != -1) { DateTime day = dateDays[index]; if (day.year != currentDate.year || day.month != currentDate.month) { SetDate(day); } else { currentDate = day; } GuiEventArgs arguments(this); DateChanged.Execute(arguments); commands->NotifyDateChanged(); commands->NotifyDateSelected(); } } } } void GuiCommonDatePickerLook::DisplayMonth(vint year, vint month) { if (YearFirst <= year && year <= YearLast && 1 <= month && month <= 12) { preventComboEvent = true; comboYear->SetSelectedIndex(year - YearFirst); comboMonth->SetSelectedIndex(month - 1); preventComboEvent = false; } vint yearPrev = year, yearNext = year, monthPrev = month, monthNext = month; StepPreviousMonth(yearPrev, monthPrev); StepNextMonth(yearNext, monthNext); vint countPrev = GetDayCountForMonth(yearPrev, monthPrev); vint count = GetDayCountForMonth(year, month); vint countNext = GetDayCountForMonth(yearNext, monthNext); DateTime firstDay = DateTime::FromDateTime(year, month, 1); vint showPrev = firstDay.dayOfWeek; if (showPrev == 0) showPrev = DaysOfWeek; vint show = count; vint showNext = DaysOfWeek*DayRows - showPrev - show; vint index = 0; for (vint i = 0; i < showPrev; i++) { DateTime day = DateTime::FromDateTime(yearPrev, monthPrev, countPrev - (showPrev - i - 1)); SetDay(day, index, false); } for (vint i = 0; i < show; i++) { DateTime day = DateTime::FromDateTime(year, month, i + 1); SetDay(day, index, true); } for (vint i = 0; i < showNext; i++) { DateTime day = DateTime::FromDateTime(yearNext, monthNext, i + 1); SetDay(day, index, false); } } void GuiCommonDatePickerLook::SelectDay(vint day) { for (vint i = 0; i < dateDays.Count(); i++) { const DateTime& dt = dateDays[i]; if (dt.year == currentDate.year && dt.month == currentDate.month && dt.day == day) { preventButtonEvent = true; buttonDays[i]->SetSelected(true); preventButtonEvent = false; break; } } } GuiCommonDatePickerLook::GuiCommonDatePickerLook(Color _backgroundColor, Color _primaryTextColor, Color _secondaryTextColor) :backgroundColor(_backgroundColor) , primaryTextColor(_primaryTextColor) , secondaryTextColor(_secondaryTextColor) { DateChanged.SetAssociatedComposition(this); SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); GuiTableComposition* monthTable = 0; GuiTableComposition* dayTable = 0; { listYears = new GuiTextList(theme::ThemeName::TextList); listYears->SetHorizontalAlwaysVisible(false); for (vint i = YearFirst; i <= YearLast; i++) { listYears->GetItems().Add(new list::TextItem(itow(i))); } comboYear = new GuiComboBoxListControl(theme::ThemeName::ComboBox, listYears); comboYear->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 2, 0)); comboYear->SelectedIndexChanged.AttachMethod(this, &GuiCommonDatePickerLook::comboYearMonth_SelectedIndexChanged); } { listMonths = new GuiTextList(theme::ThemeName::TextList); listMonths->SetHorizontalAlwaysVisible(false); comboMonth = new GuiComboBoxListControl(theme::ThemeName::ComboBox, listMonths); comboMonth->GetBoundsComposition()->SetAlignmentToParent(Margin(2, 0, 0, 0)); comboMonth->SelectedIndexChanged.AttachMethod(this, &GuiCommonDatePickerLook::comboYearMonth_SelectedIndexChanged); } { monthTable = new GuiTableComposition; monthTable->SetAlignmentToParent(Margin(0, 0, 0, 0)); monthTable->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); monthTable->SetRowsAndColumns(1, 2); monthTable->SetRowOption(0, GuiCellOption::MinSizeOption()); monthTable->SetColumnOption(0, GuiCellOption::PercentageOption(0.5)); monthTable->SetColumnOption(1, GuiCellOption::PercentageOption(0.5)); { GuiCellComposition* cell = new GuiCellComposition; monthTable->AddChild(cell); cell->SetSite(0, 0, 1, 1); cell->AddChild(comboYear->GetBoundsComposition()); } { GuiCellComposition* cell = new GuiCellComposition; monthTable->AddChild(cell); cell->SetSite(0, 1, 1, 1); cell->AddChild(comboMonth->GetBoundsComposition()); } } { dayTable = new GuiTableComposition; dayTable->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); dayTable->SetCellPadding(4); dayTable->SetRowsAndColumns(DayRows + DayRowStart, DaysOfWeek); for (vint i = 0; i < DayRowStart; i++) { dayTable->SetRowOption(i, GuiCellOption::MinSizeOption()); } for (vint i = 0; i < DayRows; i++) { dayTable->SetRowOption(i + DayRowStart, GuiCellOption::PercentageOption(1.0)); } for (vint i = 0; i < DaysOfWeek; i++) { dayTable->SetColumnOption(i, GuiCellOption::PercentageOption(1.0)); } { GuiCellComposition* cell = new GuiCellComposition; dayTable->AddChild(cell); cell->SetSite(0, 0, 1, DaysOfWeek); cell->AddChild(monthTable); } labelDaysOfWeek.Resize(7); for (vint i = 0; i < DaysOfWeek; i++) { GuiCellComposition* cell = new GuiCellComposition; dayTable->AddChild(cell); cell->SetSite(1, i, 1, 1); GuiSolidLabelElement* element = GuiSolidLabelElement::Create(); element->SetAlignments(Alignment::Center, Alignment::Center); element->SetColor(primaryTextColor); labelDaysOfWeek[i] = element; cell->SetOwnedElement(element); } buttonDays.Resize(DaysOfWeek*DayRows); labelDays.Resize(DaysOfWeek*DayRows); dateDays.Resize(DaysOfWeek*DayRows); auto dayMutexController = new GuiSelectableButton::MutexGroupController; AddComponent(dayMutexController); for (vint i = 0; i < DaysOfWeek; i++) { for (vint j = 0; j < DayRows; j++) { GuiCellComposition* cell = new GuiCellComposition; dayTable->AddChild(cell); cell->SetSite(j + DayRowStart, i, 1, 1); GuiSelectableButton* button = new GuiSelectableButton(theme::ThemeName::CheckBox); button->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); button->SetGroupController(dayMutexController); button->SelectedChanged.AttachMethod(this, &GuiCommonDatePickerLook::buttonDay_SelectedChanged); cell->AddChild(button->GetBoundsComposition()); buttonDays[j*DaysOfWeek + i] = button; GuiSolidLabelElement* element = GuiSolidLabelElement::Create(); element->SetAlignments(Alignment::Center, Alignment::Center); element->SetText(L"0"); labelDays[j*DaysOfWeek + i] = element; GuiBoundsComposition* elementBounds = new GuiBoundsComposition; elementBounds->SetOwnedElement(element); elementBounds->SetAlignmentToParent(Margin(0, 0, 0, 0)); elementBounds->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElement); button->GetContainerComposition()->AddChild(elementBounds); } } } { GuiSolidBackgroundElement* element = GuiSolidBackgroundElement::Create(); element->SetColor(backgroundColor); dayTable->SetOwnedElement(element); } dayTable->SetAlignmentToParent(Margin(0, 0, 0, 0)); AddChild(dayTable); SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); SetFont(font); } GuiCommonDatePickerLook::~GuiCommonDatePickerLook() { FinalizeInstanceRecursively(this); } controls::IDatePickerCommandExecutor* GuiCommonDatePickerLook::GetCommands() { return commands; } void GuiCommonDatePickerLook::SetCommands(controls::IDatePickerCommandExecutor* value) { commands = value; } TemplateProperty GuiCommonDatePickerLook::GetDateButtonTemplate() { return dateButtonTemplate; } void GuiCommonDatePickerLook::SetDateButtonTemplate(const TemplateProperty& value) { dateButtonTemplate = value; FOREACH(GuiSelectableButton*, button, buttonDays) { button->SetControlTemplate(value); } } TemplateProperty GuiCommonDatePickerLook::GetDateTextListTemplate() { return dateTextListTemplate; } void GuiCommonDatePickerLook::SetDateTextListTemplate(const TemplateProperty& value) { dateTextListTemplate = value; listYears->SetControlTemplate(value); listMonths->SetControlTemplate(value); } TemplateProperty GuiCommonDatePickerLook::GetDateComboBoxTemplate() { return dateComboBoxTemplate; } void GuiCommonDatePickerLook::SetDateComboBoxTemplate(const TemplateProperty& value) { dateComboBoxTemplate = value; comboYear->SetControlTemplate(value); comboMonth->SetControlTemplate(value); } const Locale& GuiCommonDatePickerLook::GetDateLocale() { return dateLocale; } void GuiCommonDatePickerLook::SetDateLocale(const Locale& value) { if (dateLocale != value) { dateLocale = value; for (vint i = 0; i < DaysOfWeek; i++) { labelDaysOfWeek[i]->SetText(dateLocale.GetShortDayOfWeekName(i)); } listMonths->GetItems().Clear(); for (vint i = 1; i <= 12; i++) { listMonths->GetItems().Add(new list::TextItem(dateLocale.GetLongMonthName(i))); } SetDate(currentDate); } } const DateTime& GuiCommonDatePickerLook::GetDate() { return currentDate; } void GuiCommonDatePickerLook::SetDate(const DateTime& value) { currentDate = value; DisplayMonth(value.year, value.month); SelectDay(value.day); } const FontProperties& GuiCommonDatePickerLook::GetFont() { return font; } void GuiCommonDatePickerLook::SetFont(const FontProperties& value) { if (font != value) { font = value; comboYear->SetFont(value); listYears->SetFont(value); comboMonth->SetFont(value); listMonths->SetFont(value); FOREACH(GuiSolidLabelElement*, label, From(labelDaysOfWeek).Concat(labelDays)) { label->SetFont(value); } } } /*********************************************************************** GuiCommonScrollViewLook ***********************************************************************/ void GuiCommonScrollViewLook::UpdateTable() { if (horizontalScroll->GetVisible()) { tableComposition->SetRowOption(1, GuiCellOption::AbsoluteOption(defaultScrollSize)); } else { tableComposition->SetRowOption(1, GuiCellOption::AbsoluteOption(0)); } if (verticalScroll->GetVisible()) { tableComposition->SetColumnOption(1, GuiCellOption::AbsoluteOption(defaultScrollSize)); } else { tableComposition->SetColumnOption(1, GuiCellOption::AbsoluteOption(0)); } tableComposition->UpdateCellBounds(); } void GuiCommonScrollViewLook::hScroll_OnVisibleChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { UpdateTable(); } void GuiCommonScrollViewLook::vScroll_OnVisibleChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { UpdateTable(); } GuiCommonScrollViewLook::GuiCommonScrollViewLook(vint _defaultScrollSize) :defaultScrollSize(_defaultScrollSize) { SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); horizontalScroll = new GuiScroll(theme::ThemeName::HScroll); horizontalScroll->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); horizontalScroll->SetEnabled(false); verticalScroll = new GuiScroll(theme::ThemeName::VScroll); verticalScroll->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); verticalScroll->SetEnabled(false); tableComposition = new GuiTableComposition; AddChild(tableComposition); tableComposition->SetAlignmentToParent(Margin(0, 0, 0, 0)); tableComposition->SetRowsAndColumns(2, 2); tableComposition->SetRowOption(0, GuiCellOption::PercentageOption(1.0)); tableComposition->SetRowOption(1, GuiCellOption::MinSizeOption()); tableComposition->SetColumnOption(0, GuiCellOption::PercentageOption(1.0)); tableComposition->SetColumnOption(1, GuiCellOption::MinSizeOption()); UpdateTable(); { GuiCellComposition* cell = new GuiCellComposition; tableComposition->AddChild(cell); cell->SetSite(1, 0, 1, 1); cell->AddChild(horizontalScroll->GetBoundsComposition()); } { GuiCellComposition* cell = new GuiCellComposition; tableComposition->AddChild(cell); cell->SetSite(0, 1, 1, 1); cell->AddChild(verticalScroll->GetBoundsComposition()); } containerCellComposition = new GuiCellComposition; tableComposition->AddChild(containerCellComposition); containerCellComposition->SetSite(0, 0, 1, 1); containerComposition = new GuiBoundsComposition; containerComposition->SetAlignmentToParent(Margin(0, 0, 0, 0)); containerCellComposition->AddChild(containerComposition); horizontalScroll->VisibleChanged.AttachMethod(this, &GuiCommonScrollViewLook::hScroll_OnVisibleChanged); verticalScroll->VisibleChanged.AttachMethod(this, &GuiCommonScrollViewLook::vScroll_OnVisibleChanged); UpdateTable(); } GuiCommonScrollViewLook::~GuiCommonScrollViewLook() { } controls::GuiScroll* GuiCommonScrollViewLook::GetHScroll() { return horizontalScroll; } controls::GuiScroll* GuiCommonScrollViewLook::GetVScroll() { return verticalScroll; } compositions::GuiGraphicsComposition* GuiCommonScrollViewLook::GetContainerComposition() { return containerComposition; } TemplateProperty GuiCommonScrollViewLook::GetHScrollTemplate() { return hScrollTemplate; } void GuiCommonScrollViewLook::SetHScrollTemplate(const TemplateProperty& value) { hScrollTemplate = value; horizontalScroll->SetControlTemplate(value); } TemplateProperty GuiCommonScrollViewLook::GetVScrollTemplate() { return vScrollTemplate; } void GuiCommonScrollViewLook::SetVScrollTemplate(const TemplateProperty& value) { vScrollTemplate = value; verticalScroll->SetControlTemplate(value); } /*********************************************************************** GuiCommonScrollBehavior ***********************************************************************/ void GuiCommonScrollBehavior::SetScroll(vint totalPixels, vint newOffset) { vint totalSize = scrollTemplate->GetTotalSize(); double ratio = (double)newOffset / totalPixels; vint newPosition = (vint)round(ratio * totalSize); vint offset1 = (vint)round(((double)newPosition / totalSize) * totalPixels); vint offset2 = (vint)round(((double)(newPosition + 1)) / totalSize * totalPixels); vint delta1 = offset1 - newOffset; vint delta2 = offset2 - newOffset; if (delta1 < 0) { delta1 = -delta1; } if (delta2 < 0) { delta2 = -delta2; } if (delta1 < delta2) { scrollTemplate->GetCommands()->SetPosition(newPosition); } else { scrollTemplate->GetCommands()->SetPosition(newPosition + 1); } } void GuiCommonScrollBehavior::AttachHandle(compositions::GuiGraphicsComposition* handle) { handle->GetEventReceiver()->leftButtonDown.AttachLambda([=](GuiGraphicsComposition*, GuiMouseEventArgs& arguments) { if (scrollTemplate->GetVisuallyEnabled()) { dragging = true; location.x = arguments.x; location.y = arguments.y; } }); handle->GetEventReceiver()->leftButtonUp.AttachLambda([=](GuiGraphicsComposition*, GuiMouseEventArgs&) { if (scrollTemplate->GetVisuallyEnabled()) { dragging = false; } }); } GuiCommonScrollBehavior::GuiCommonScrollBehavior() { } GuiCommonScrollBehavior::~GuiCommonScrollBehavior() { } void GuiCommonScrollBehavior::AttachScrollTemplate(GuiScrollTemplate* value) { scrollTemplate = value; } void GuiCommonScrollBehavior::AttachDecreaseButton(controls::GuiButton* button) { button->Clicked.AttachLambda([=](GuiGraphicsComposition*, GuiEventArgs&) { scrollTemplate->GetCommands()->SmallDecrease(); }); } void GuiCommonScrollBehavior::AttachIncreaseButton(controls::GuiButton* button) { button->Clicked.AttachLambda([=](GuiGraphicsComposition*, GuiEventArgs&) { scrollTemplate->GetCommands()->SmallIncrease(); }); } void GuiCommonScrollBehavior::AttachHorizontalScrollHandle(compositions::GuiPartialViewComposition* partialView) { partialView->GetParent()->GetEventReceiver()->leftButtonDown.AttachLambda([=](GuiGraphicsComposition*, GuiMouseEventArgs& arguments) { if (scrollTemplate->GetVisuallyEnabled()) { if (arguments.x < partialView->GetBounds().x1) { scrollTemplate->GetCommands()->BigDecrease(); } else if (arguments.x >= partialView->GetBounds().x2) { scrollTemplate->GetCommands()->BigIncrease(); } } }); AttachHorizontalTrackerHandle(partialView); } void GuiCommonScrollBehavior::AttachVerticalScrollHandle(compositions::GuiPartialViewComposition* partialView) { partialView->GetParent()->GetEventReceiver()->leftButtonDown.AttachLambda([=](GuiGraphicsComposition*, GuiMouseEventArgs& arguments) { if (scrollTemplate->GetVisuallyEnabled()) { if (arguments.y < partialView->GetBounds().y1) { scrollTemplate->GetCommands()->BigDecrease(); } else if (arguments.y >= partialView->GetBounds().y2) { scrollTemplate->GetCommands()->BigIncrease(); } } }); AttachVerticalTrackerHandle(partialView); } void GuiCommonScrollBehavior::AttachHorizontalTrackerHandle(compositions::GuiPartialViewComposition* partialView) { partialView->GetEventReceiver()->mouseMove.AttachLambda([=](GuiGraphicsComposition*, GuiMouseEventArgs& arguments) { if (dragging) { auto bounds = partialView->GetParent()->GetBounds(); vint totalPixels = bounds.x2 - bounds.x1; vint currentOffset = partialView->GetBounds().x1; vint newOffset = currentOffset + (arguments.x - location.x); SetScroll(totalPixels, newOffset); } }); AttachHandle(partialView); } void GuiCommonScrollBehavior::AttachVerticalTrackerHandle(compositions::GuiPartialViewComposition* partialView) { partialView->GetEventReceiver()->mouseMove.AttachLambda([=](GuiGraphicsComposition*, GuiMouseEventArgs& arguments) { if (dragging) { auto bounds = partialView->GetParent()->GetBounds(); vint totalPixels = bounds.y2 - bounds.y1; vint currentOffset = partialView->GetBounds().y1; vint newOffset = currentOffset + (arguments.y - location.y); SetScroll(totalPixels, newOffset); } }); AttachHandle(partialView); } vint GuiCommonScrollBehavior::GetHorizontalTrackerHandlerPosition(compositions::GuiBoundsComposition* handle, vint totalSize, vint pageSize, vint position) { vint width = handle->GetParent()->GetBounds().Width() - handle->GetBounds().Width(); vint max = totalSize - pageSize; return max == 0 ? 0 : width * position / max; } vint GuiCommonScrollBehavior::GetVerticalTrackerHandlerPosition(compositions::GuiBoundsComposition* handle, vint totalSize, vint pageSize, vint position) { vint height = handle->GetParent()->GetBounds().Height() - handle->GetBounds().Height(); vint max = totalSize - pageSize; return max == 0 ? 0 : height * position / max; } } } } /*********************************************************************** .\CONTROLS\TOOLSTRIPPACKAGE\GUIMENUCONTROLS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace compositions; /*********************************************************************** IGuiMenuService ***********************************************************************/ const wchar_t* const IGuiMenuService::Identifier = L"vl::presentation::controls::IGuiMenuService"; const wchar_t* const IGuiMenuDropdownProvider::Identifier = L"vl::presentation::controls::IGuiMenuDropdownProvider"; IGuiMenuService::IGuiMenuService() :openingMenu(0) { } void IGuiMenuService::MenuItemExecuted() { if(openingMenu) { openingMenu->Hide(); } if(GetParentMenuService()) { GetParentMenuService()->MenuItemExecuted(); } } GuiMenu* IGuiMenuService::GetOpeningMenu() { return openingMenu; } void IGuiMenuService::MenuOpened(GuiMenu* menu) { if(openingMenu!=menu && openingMenu) { openingMenu->Hide(); } openingMenu=menu; } void IGuiMenuService::MenuClosed(GuiMenu* menu) { if(openingMenu==menu) { openingMenu=0; } } /*********************************************************************** GuiMenu ***********************************************************************/ void GuiMenu::BeforeControlTemplateUninstalled_() { } void GuiMenu::AfterControlTemplateInstalled_(bool initialize) { } IGuiMenuService* GuiMenu::GetParentMenuService() { return parentMenuService; } IGuiMenuService::Direction GuiMenu::GetPreferredDirection() { return IGuiMenuService::Vertical; } bool GuiMenu::IsActiveState() { return true; } bool GuiMenu::IsSubMenuActivatedByMouseDown() { return false; } void GuiMenu::MenuItemExecuted() { IGuiMenuService::MenuItemExecuted(); Hide(); } void GuiMenu::OnWindowOpened(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if(parentMenuService) { parentMenuService->MenuOpened(this); } } void GuiMenu::OnDeactivatedAltHost() { Hide(); } void GuiMenu::MouseClickedOnOtherWindow(GuiWindow* window) { GuiMenu* targetMenu=dynamic_cast(window); if(!targetMenu) { Hide(); } } void GuiMenu::OnWindowClosed(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if(parentMenuService) { parentMenuService->MenuClosed(this); GuiMenu* openingSubMenu=GetOpeningMenu(); if(openingSubMenu) { openingSubMenu->Hide(); } } } GuiMenu::GuiMenu(theme::ThemeName themeName, GuiControl* _owner) :GuiPopup(themeName) , owner(_owner) , parentMenuService(0) { GetNativeWindow()->SetAlwaysPassFocusToParent(true); UpdateMenuService(); WindowOpened.AttachMethod(this, &GuiMenu::OnWindowOpened); WindowClosed.AttachMethod(this, &GuiMenu::OnWindowClosed); } GuiMenu::~GuiMenu() { } void GuiMenu::UpdateMenuService() { if(owner) { parentMenuService=owner->QueryTypedService(); } } IDescriptable* GuiMenu::QueryService(const WString& identifier) { if(identifier==IGuiMenuService::Identifier) { return (IGuiMenuService*)this; } else { return GuiPopup::QueryService(identifier); } } /*********************************************************************** GuiMenuBar ***********************************************************************/ IGuiMenuService* GuiMenuBar::GetParentMenuService() { return 0; } IGuiMenuService::Direction GuiMenuBar::GetPreferredDirection() { return IGuiMenuService::Horizontal; } bool GuiMenuBar::IsActiveState() { return GetOpeningMenu()!=0; } bool GuiMenuBar::IsSubMenuActivatedByMouseDown() { return true; } GuiMenuBar::GuiMenuBar(theme::ThemeName themeName) :GuiControl(themeName) { } GuiMenuBar::~GuiMenuBar() { } IDescriptable* GuiMenuBar::QueryService(const WString& identifier) { if(identifier==IGuiMenuService::Identifier) { return (IGuiMenuService*)this; } else { return GuiControl::QueryService(identifier); } } /*********************************************************************** GuiMenuButton ***********************************************************************/ void GuiMenuButton::BeforeControlTemplateUninstalled_() { auto host = GetSubMenuHost(); host->Clicked.Detach(hostClickedHandler); host->GetBoundsComposition()->GetEventReceiver()->mouseEnter.Detach(hostMouseEnterHandler); hostClickedHandler = nullptr; hostMouseEnterHandler = nullptr; } void GuiMenuButton::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); auto host = GetSubMenuHost(); ct->SetSubMenuOpening(GetSubMenuOpening()); ct->SetLargeImage(largeImage); ct->SetImage(image); ct->SetShortcutText(shortcutText); ct->SetSubMenuExisting(subMenu != nullptr); hostClickedHandler = host->Clicked.AttachMethod(this, &GuiMenuButton::OnClicked); hostMouseEnterHandler = host->GetBoundsComposition()->GetEventReceiver()->mouseEnter.AttachMethod(this, &GuiMenuButton::OnMouseEnter); } GuiButton* GuiMenuButton::GetSubMenuHost() { GuiButton* button = GetControlTemplateObject(true)->GetSubMenuHost(); return button ? button : this; } void GuiMenuButton::OpenSubMenuInternal() { if(!GetSubMenuOpening()) { if(ownerMenuService) { GuiMenu* openingSiblingMenu=ownerMenuService->GetOpeningMenu(); if(openingSiblingMenu) { openingSiblingMenu->Hide(); } } SetSubMenuOpening(true); } } void GuiMenuButton::OnParentLineChanged() { GuiButton::OnParentLineChanged(); ownerMenuService=QueryTypedService(); if(ownerMenuService) { SetClickOnMouseUp(!ownerMenuService->IsSubMenuActivatedByMouseDown()); } if(subMenu) { subMenu->UpdateMenuService(); } } bool GuiMenuButton::IsAltAvailable() { return true; } compositions::IGuiAltActionHost* GuiMenuButton::GetActivatingAltHost() { if (subMenu) { return subMenu->QueryTypedService(); } return 0; } void GuiMenuButton::OnSubMenuWindowOpened(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { SubMenuOpeningChanged.Execute(GetNotifyEventArguments()); GetControlTemplateObject(true)->SetSubMenuOpening(true); } void GuiMenuButton::OnSubMenuWindowClosed(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { SubMenuOpeningChanged.Execute(GetNotifyEventArguments()); GetControlTemplateObject(true)->SetSubMenuOpening(false); } void GuiMenuButton::OnMouseEnter(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if(GetVisuallyEnabled()) { if(cascadeAction && ownerMenuService && ownerMenuService->IsActiveState()) { OpenSubMenuInternal(); } } } void GuiMenuButton::OnClicked(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if(GetVisuallyEnabled()) { BeforeSubMenuOpening.Execute(GetNotifyEventArguments()); if(GetSubMenu()) { OpenSubMenuInternal(); } else if(ownerMenuService) { ownerMenuService->MenuItemExecuted(); } } } IGuiMenuService::Direction GuiMenuButton::GetSubMenuDirection() { return ownerMenuService?ownerMenuService->GetPreferredDirection():IGuiMenuService::Horizontal; } GuiMenu* GuiMenuButton::ProvideDropdownMenu() { return GetSubMenu(); } GuiMenuButton::GuiMenuButton(theme::ThemeName themeName) :GuiSelectableButton(themeName) ,subMenu(0) ,ownedSubMenu(false) ,ownerMenuService(0) ,cascadeAction(true) { SetAutoSelection(false); BeforeSubMenuOpening.SetAssociatedComposition(boundsComposition); SubMenuOpeningChanged.SetAssociatedComposition(boundsComposition); LargeImageChanged.SetAssociatedComposition(boundsComposition); ImageChanged.SetAssociatedComposition(boundsComposition); ShortcutTextChanged.SetAssociatedComposition(boundsComposition); } GuiMenuButton::~GuiMenuButton() { if(subMenu && ownedSubMenu) { delete subMenu; } } Ptr GuiMenuButton::GetLargeImage() { return largeImage; } void GuiMenuButton::SetLargeImage(Ptr value) { if (largeImage != value) { largeImage = value; GetControlTemplateObject(true)->SetLargeImage(largeImage); LargeImageChanged.Execute(GetNotifyEventArguments()); } } Ptr GuiMenuButton::GetImage() { return image; } void GuiMenuButton::SetImage(Ptr value) { if (image != value) { image = value; GetControlTemplateObject(true)->SetImage(image); ImageChanged.Execute(GetNotifyEventArguments()); } } const WString& GuiMenuButton::GetShortcutText() { return shortcutText; } void GuiMenuButton::SetShortcutText(const WString& value) { if (shortcutText != value) { shortcutText = value; GetControlTemplateObject(true)->SetShortcutText(shortcutText); ShortcutTextChanged.Execute(GetNotifyEventArguments()); } } bool GuiMenuButton::IsSubMenuExists() { return subMenu!=0; } GuiMenu* GuiMenuButton::GetSubMenu() { return subMenu; } GuiMenu* GuiMenuButton::CreateSubMenu(TemplateProperty subMenuTemplate) { if (!subMenu) { GuiMenu* newSubMenu = new GuiMenu(theme::ThemeName::Menu, this); newSubMenu->SetControlTemplate(subMenuTemplate ? subMenuTemplate : GetControlTemplateObject(true)->GetSubMenuTemplate()); SetSubMenu(newSubMenu, true); } return subMenu; } void GuiMenuButton::SetSubMenu(GuiMenu* value, bool owned) { if(subMenu) { if(ownedSubMenu) { delete subMenu; } } subMenu=value; ownedSubMenu=owned; if(subMenu) { subMenu->WindowOpened.AttachMethod(this, &GuiMenuButton::OnSubMenuWindowOpened); subMenu->WindowClosed.AttachMethod(this, &GuiMenuButton::OnSubMenuWindowClosed); } GetControlTemplateObject(true)->SetSubMenuExisting(subMenu != nullptr); } void GuiMenuButton::DestroySubMenu() { if(subMenu) { if(ownedSubMenu) { delete subMenu; } subMenu=0; ownedSubMenu=false; GetControlTemplateObject(true)->SetSubMenuExisting(false); } } bool GuiMenuButton::GetOwnedSubMenu() { return subMenu && ownedSubMenu; } bool GuiMenuButton::GetSubMenuOpening() { if(subMenu) { return subMenu->GetOpening(); } else { return false; } } void GuiMenuButton::SetSubMenuOpening(bool value) { if(subMenu) { if(value) { subMenu->SetClientSize(preferredMenuClientSize); IGuiMenuService::Direction direction=GetSubMenuDirection(); subMenu->ShowPopup(GetSubMenuHost(), direction==IGuiMenuService::Horizontal); } else { subMenu->Close(); } } } Size GuiMenuButton::GetPreferredMenuClientSize() { return preferredMenuClientSize; } void GuiMenuButton::SetPreferredMenuClientSize(Size value) { preferredMenuClientSize=value; } bool GuiMenuButton::GetCascadeAction() { return cascadeAction; } void GuiMenuButton::SetCascadeAction(bool value) { cascadeAction=value; } IDescriptable* GuiMenuButton::QueryService(const WString& identifier) { if (identifier == IGuiMenuDropdownProvider::Identifier) { return (IGuiMenuDropdownProvider*)this; } else { return GuiSelectableButton::QueryService(identifier); } } } } } /*********************************************************************** .\CONTROLS\GUIDIALOGS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace elements; using namespace compositions; using namespace collections; using namespace reflection::description; /*********************************************************************** GuiDialogBase ***********************************************************************/ GuiWindow* GuiDialogBase::GetHostWindow() { if (rootObject) { if (auto control = dynamic_cast(rootObject)) { if (auto host = control->GetRelatedControlHost()) { return dynamic_cast(host); } } else if (auto composition = dynamic_cast(rootObject)) { if (auto host = composition->GetRelatedControlHost()) { return dynamic_cast(host); } } } return nullptr; } GuiDialogBase::GuiDialogBase() { } GuiDialogBase::~GuiDialogBase() { } void GuiDialogBase::Attach(GuiInstanceRootObject* _rootObject) { rootObject = _rootObject; } void GuiDialogBase::Detach(GuiInstanceRootObject* _rootObject) { rootObject = nullptr; } /*********************************************************************** GuiMessageDialog ***********************************************************************/ GuiMessageDialog::GuiMessageDialog() { } GuiMessageDialog::~GuiMessageDialog() { } INativeDialogService::MessageBoxButtonsInput GuiMessageDialog::GetInput() { return input; } void GuiMessageDialog::SetInput(INativeDialogService::MessageBoxButtonsInput value) { input = value; } INativeDialogService::MessageBoxDefaultButton GuiMessageDialog::GetDefaultButton() { return defaultButton; } void GuiMessageDialog::SetDefaultButton(INativeDialogService::MessageBoxDefaultButton value) { defaultButton = value; } INativeDialogService::MessageBoxIcons GuiMessageDialog::GetIcon() { return icon; } void GuiMessageDialog::SetIcon(INativeDialogService::MessageBoxIcons value) { icon = value; } INativeDialogService::MessageBoxModalOptions GuiMessageDialog::GetModalOption() { return modalOption; } void GuiMessageDialog::SetModalOption(INativeDialogService::MessageBoxModalOptions value) { modalOption = value; } const WString& GuiMessageDialog::GetText() { return text; } void GuiMessageDialog::SetText(const WString& value) { text = value; } const WString& GuiMessageDialog::GetTitle() { return title; } void GuiMessageDialog::SetTitle(const WString& value) { title = value; } INativeDialogService::MessageBoxButtonsOutput GuiMessageDialog::ShowDialog() { auto service = GetCurrentController()->DialogService(); return service->ShowMessageBox(GetHostWindow()->GetNativeWindow(), text, title, input, defaultButton, icon, modalOption); } /*********************************************************************** GuiColorDialog ***********************************************************************/ GuiColorDialog::GuiColorDialog() { for (vint i = 0; i < 16; i++) { customColors.Add(Color()); } } GuiColorDialog::~GuiColorDialog() { } bool GuiColorDialog::GetEnabledCustomColor() { return enabledCustomColor; } void GuiColorDialog::SetEnabledCustomColor(bool value) { enabledCustomColor = value; } bool GuiColorDialog::GetOpenedCustomColor() { return openedCustomColor; } void GuiColorDialog::SetOpenedCustomColor(bool value) { openedCustomColor = value; } Color GuiColorDialog::GetSelectedColor() { return selectedColor; } void GuiColorDialog::SetSelectedColor(Color value) { if (selectedColor != value) { selectedColor = value; SelectedColorChanged.Execute(GuiEventArgs()); } } collections::List& GuiColorDialog::GetCustomColors() { return customColors; } bool GuiColorDialog::ShowDialog() { Array colors; CopyFrom(colors, customColors); colors.Resize(16); INativeDialogService::ColorDialogCustomColorOptions options = !enabledCustomColor ? INativeDialogService::CustomColorDisabled : !openedCustomColor ? INativeDialogService::CustomColorEnabled : INativeDialogService::CustomColorOpened; auto service = GetCurrentController()->DialogService(); if (!service->ShowColorDialog(GetHostWindow()->GetNativeWindow(), selectedColor, showSelection, options, &colors[0])) { return false; } CopyFrom(customColors, colors); SelectedColorChanged.Execute(GuiEventArgs()); return true; } /*********************************************************************** GuiFontDialog ***********************************************************************/ GuiFontDialog::GuiFontDialog() { } GuiFontDialog::~GuiFontDialog() { } const FontProperties& GuiFontDialog::GetSelectedFont() { return selectedFont; } void GuiFontDialog::SetSelectedFont(const FontProperties& value) { if (selectedFont != value) { selectedFont = value; SelectedFontChanged.Execute(GuiEventArgs()); } } Color GuiFontDialog::GetSelectedColor() { return selectedColor; } void GuiFontDialog::SetSelectedColor(Color value) { if (selectedColor != value) { selectedColor = value; SelectedColorChanged.Execute(GuiEventArgs()); } } bool GuiFontDialog::GetShowSelection() { return showSelection; } void GuiFontDialog::SetShowSelection(bool value) { showSelection = value; } bool GuiFontDialog::GetShowEffect() { return showEffect; } void GuiFontDialog::SetShowEffect(bool value) { showEffect = value; } bool GuiFontDialog::GetForceFontExist() { return forceFontExist; } void GuiFontDialog::SetForceFontExist(bool value) { forceFontExist = value; } bool GuiFontDialog::ShowDialog() { auto service = GetCurrentController()->DialogService(); if (!service->ShowFontDialog(GetHostWindow()->GetNativeWindow(), selectedFont, selectedColor, showSelection, showEffect, forceFontExist)) { return false; } SelectedColorChanged.Execute(GuiEventArgs()); SelectedFontChanged.Execute(GuiEventArgs()); return true; } /*********************************************************************** GuiFileDialogBase ***********************************************************************/ GuiFileDialogBase::GuiFileDialogBase() { } GuiFileDialogBase::~GuiFileDialogBase() { } const WString& GuiFileDialogBase::GetFilter() { return filter; } void GuiFileDialogBase::SetFilter(const WString& value) { filter = value; } vint GuiFileDialogBase::GetFilterIndex() { return filterIndex; } void GuiFileDialogBase::SetFilterIndex(vint value) { if (filterIndex != value) { filterIndex = value; FilterIndexChanged.Execute(GuiEventArgs()); } } bool GuiFileDialogBase::GetEnabledPreview() { return enabledPreview; } void GuiFileDialogBase::SetEnabledPreview(bool value) { enabledPreview = value; } WString GuiFileDialogBase::GetTitle() { return title; } void GuiFileDialogBase::SetTitle(const WString& value) { title = value; } WString GuiFileDialogBase::GetFileName() { return fileName; } void GuiFileDialogBase::SetFileName(const WString& value) { if (fileName != value) { FileNameChanged.Execute(GuiEventArgs()); } } WString GuiFileDialogBase::GetDirectory() { return directory; } void GuiFileDialogBase::SetDirectory(const WString& value) { directory = value; } WString GuiFileDialogBase::GetDefaultExtension() { return defaultExtension; } void GuiFileDialogBase::SetDefaultExtension(const WString& value) { defaultExtension = value; } INativeDialogService::FileDialogOptions GuiFileDialogBase::GetOptions() { return options; } void GuiFileDialogBase::SetOptions(INativeDialogService::FileDialogOptions value) { options = value; } /*********************************************************************** GuiOpenFileDialog ***********************************************************************/ GuiOpenFileDialog::GuiOpenFileDialog() { } GuiOpenFileDialog::~GuiOpenFileDialog() { } collections::List& GuiOpenFileDialog::GetFileNames() { return fileNames; } bool GuiOpenFileDialog::ShowDialog() { fileNames.Clear(); auto service = GetCurrentController()->DialogService(); if (!service->ShowFileDialog( GetHostWindow()->GetNativeWindow(), fileNames, filterIndex, (enabledPreview ? INativeDialogService::FileDialogOpenPreview : INativeDialogService::FileDialogOpen), title, fileName, directory, defaultExtension, filter, options)) { return false; } if (fileNames.Count() > 0) { fileName = fileNames[0]; FileNameChanged.Execute(GuiEventArgs()); FilterIndexChanged.Execute(GuiEventArgs()); } return true; } /*********************************************************************** GuiSaveFileDialog ***********************************************************************/ GuiSaveFileDialog::GuiSaveFileDialog() { } GuiSaveFileDialog::~GuiSaveFileDialog() { } bool GuiSaveFileDialog::ShowDialog() { List fileNames; auto service = GetCurrentController()->DialogService(); if (!service->ShowFileDialog( GetHostWindow()->GetNativeWindow(), fileNames, filterIndex, (enabledPreview ? INativeDialogService::FileDialogSavePreview : INativeDialogService::FileDialogSave), title, fileName, directory, defaultExtension, filter, options)) { return false; } if (fileNames.Count() > 0) { fileName = fileNames[0]; FileNameChanged.Execute(GuiEventArgs()); FilterIndexChanged.Execute(GuiEventArgs()); } return true; } } } } /*********************************************************************** .\CONTROLS\LISTCONTROLPACKAGE\GUIBINDABLEDATAGRID.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace collections; using namespace description; using namespace templates; namespace list { /*********************************************************************** DataFilterBase ***********************************************************************/ void DataFilterBase::InvokeOnProcessorChanged() { if (callback) { callback->OnProcessorChanged(); } } DataFilterBase::DataFilterBase() { } void DataFilterBase::SetCallback(IDataProcessorCallback* value) { callback = value; } /*********************************************************************** DataMultipleFilter ***********************************************************************/ DataMultipleFilter::DataMultipleFilter() { } bool DataMultipleFilter::AddSubFilter(Ptr value) { if (!value) return false; if (filters.Contains(value.Obj())) return false; filters.Add(value); value->SetCallback(callback); InvokeOnProcessorChanged(); return true; } bool DataMultipleFilter::RemoveSubFilter(Ptr value) { if (!value) return false; if (!filters.Contains(value.Obj())) return false; value->SetCallback(nullptr); filters.Remove(value.Obj()); InvokeOnProcessorChanged(); return true; } void DataMultipleFilter::SetCallback(IDataProcessorCallback* value) { DataFilterBase::SetCallback(value); for (vint i = 0; i < filters.Count(); i++) { filters[i]->SetCallback(value); } } /*********************************************************************** DataAndFilter ***********************************************************************/ DataAndFilter::DataAndFilter() { } bool DataAndFilter::Filter(const description::Value& row) { return From(filters) .All([row](Ptr filter) { return filter->Filter(row); }); } /*********************************************************************** DataOrFilter ***********************************************************************/ DataOrFilter::DataOrFilter() { } bool DataOrFilter::Filter(const description::Value& row) { return From(filters) .Any([row](Ptr filter) { return filter->Filter(row); }); } /*********************************************************************** DataNotFilter ***********************************************************************/ DataNotFilter::DataNotFilter() { } bool DataNotFilter::SetSubFilter(Ptr value) { if (filter == value) return false; if (filter) filter->SetCallback(nullptr); filter = value; if (filter) filter->SetCallback(callback); InvokeOnProcessorChanged(); return true; } void DataNotFilter::SetCallback(IDataProcessorCallback* value) { DataFilterBase::SetCallback(value); if (filter) filter->SetCallback(value); } bool DataNotFilter::Filter(const description::Value& row) { return filter ? true : !filter->Filter(row); } /*********************************************************************** DataSorterBase ***********************************************************************/ void DataSorterBase::InvokeOnProcessorChanged() { if (callback) { callback->OnProcessorChanged(); } } DataSorterBase::DataSorterBase() { } void DataSorterBase::SetCallback(IDataProcessorCallback* value) { callback = value; } /*********************************************************************** DataMultipleSorter ***********************************************************************/ DataMultipleSorter::DataMultipleSorter() { } bool DataMultipleSorter::SetLeftSorter(Ptr value) { if (leftSorter == value) return false; if (leftSorter) leftSorter->SetCallback(nullptr); leftSorter = value; if (leftSorter) leftSorter->SetCallback(callback); return true; } bool DataMultipleSorter::SetRightSorter(Ptr value) { if (rightSorter == value) return false; if (rightSorter) rightSorter->SetCallback(nullptr); rightSorter = value; if (rightSorter) rightSorter->SetCallback(callback); return true; } void DataMultipleSorter::SetCallback(IDataProcessorCallback* value) { DataSorterBase::SetCallback(value); if (leftSorter) leftSorter->SetCallback(value); if (rightSorter) rightSorter->SetCallback(value); } vint DataMultipleSorter::Compare(const description::Value& row1, const description::Value& row2) { if (leftSorter) { vint result = leftSorter->Compare(row1, row2); if (result != 0) return result; } if (rightSorter) { vint result = rightSorter->Compare(row1, row2); if (result != 0) return result; } return 0; } /*********************************************************************** DataReverseSorter ***********************************************************************/ DataReverseSorter::DataReverseSorter() { } bool DataReverseSorter::SetSubSorter(Ptr value) { if (sorter == value) return false; if (sorter) sorter->SetCallback(nullptr); sorter = value; if (sorter) sorter->SetCallback(callback); return true; } void DataReverseSorter::SetCallback(IDataProcessorCallback* value) { DataSorterBase::SetCallback(value); if (sorter) sorter->SetCallback(value); } vint DataReverseSorter::Compare(const description::Value& row1, const description::Value& row2) { return sorter ? -sorter->Compare(row1, row2) : 0; } /*********************************************************************** DataColumn ***********************************************************************/ void DataColumn::NotifyAllColumnsUpdate(bool affectItem) { if (dataProvider) { vint index = dataProvider->columns.IndexOf(this); if (index != -1) { dataProvider->columns.NotifyColumnUpdated(index, affectItem); } } } DataColumn::DataColumn() { } DataColumn::~DataColumn() { if (popup && ownPopup) { SafeDeleteControl(popup); } } WString DataColumn::GetText() { return text; } void DataColumn::SetText(const WString& value) { if (text != value) { text = value; NotifyAllColumnsUpdate(false); } } vint DataColumn::GetSize() { return size; } void DataColumn::SetSize(vint value) { if (size != value) { size = value; NotifyAllColumnsUpdate(false); } } bool DataColumn::GetOwnPopup() { return ownPopup; } void DataColumn::SetOwnPopup(bool value) { ownPopup = value; } GuiMenu* DataColumn::GetPopup() { return popup; } void DataColumn::SetPopup(GuiMenu* value) { if (popup != value) { popup = value; NotifyAllColumnsUpdate(false); } } Ptr DataColumn::GetFilter() { return associatedFilter; } void DataColumn::SetFilter(Ptr value) { if (associatedFilter) associatedFilter->SetCallback(nullptr); associatedFilter = value; if (associatedFilter) associatedFilter->SetCallback(dataProvider); NotifyAllColumnsUpdate(false); } Ptr DataColumn::GetSorter() { return associatedSorter; } void DataColumn::SetSorter(Ptr value) { if (associatedSorter) associatedSorter->SetCallback(nullptr); associatedSorter = value; if (associatedSorter) associatedSorter->SetCallback(dataProvider); NotifyAllColumnsUpdate(false); } Ptr DataColumn::GetVisualizerFactory() { return visualizerFactory; } void DataColumn::SetVisualizerFactory(Ptr value) { visualizerFactory = value; NotifyAllColumnsUpdate(true); } Ptr DataColumn::GetEditorFactory() { return editorFactory; } void DataColumn::SetEditorFactory(Ptr value) { editorFactory = value; NotifyAllColumnsUpdate(true); } WString DataColumn::GetCellText(vint row) { if (0 <= row && row < dataProvider->Count()) { return ReadProperty(dataProvider->GetBindingValue(row), textProperty); } return L""; } description::Value DataColumn::GetCellValue(vint row) { if (0 <= row && row < dataProvider->Count()) { return ReadProperty(dataProvider->GetBindingValue(row), valueProperty); } return Value(); } void DataColumn::SetCellValue(vint row, description::Value value) { if (0 <= row && row < dataProvider->Count()) { auto rowValue = dataProvider->GetBindingValue(row); WriteProperty(rowValue, valueProperty, value); dataProvider->InvokeOnItemModified(row, 1, 1); } } ItemProperty DataColumn::GetTextProperty() { return textProperty; } void DataColumn::SetTextProperty(const ItemProperty& value) { if (textProperty != value) { textProperty = value; NotifyAllColumnsUpdate(true); compositions::GuiEventArgs arguments; TextPropertyChanged.Execute(arguments); } } WritableItemProperty DataColumn::GetValueProperty() { return valueProperty; } void DataColumn::SetValueProperty(const WritableItemProperty& value) { if (valueProperty != value) { valueProperty = value; NotifyAllColumnsUpdate(true); compositions::GuiEventArgs arguments; ValuePropertyChanged.Execute(arguments); } } /*********************************************************************** DataColumns ***********************************************************************/ void DataColumns::NotifyColumnUpdated(vint index, bool affectItem) { affectItemFlag = affectItem; NotifyUpdateInternal(index, 1, 1); affectItemFlag = true; } void DataColumns::NotifyUpdateInternal(vint start, vint count, vint newCount) { dataProvider->NotifyAllColumnsUpdate(); if (affectItemFlag) { dataProvider->NotifyAllItemsUpdate(); } } bool DataColumns::QueryInsert(vint index, const Ptr& value) { return !items.Contains(value.Obj()); } void DataColumns::AfterInsert(vint index, const Ptr& value) { value->dataProvider = dataProvider; } void DataColumns::BeforeRemove(vint index, const Ptr& value) { value->dataProvider = nullptr; } DataColumns::DataColumns(DataProvider* _dataProvider) :dataProvider(_dataProvider) { } DataColumns::~DataColumns() { } /*********************************************************************** DataProvider ***********************************************************************/ void DataProvider::NotifyAllItemsUpdate() { InvokeOnItemModified(0, Count(), Count()); } void DataProvider::NotifyAllColumnsUpdate() { if (columnItemViewCallback) { columnItemViewCallback->OnColumnChanged(); } } GuiListControl::IItemProvider* DataProvider::GetItemProvider() { return this; } void DataProvider::OnProcessorChanged() { RebuildFilter(); ReorderRows(true); } void DataProvider::OnItemSourceModified(vint start, vint count, vint newCount) { if (!currentSorter && !currentFilter && count == newCount) { InvokeOnItemModified(start, count, newCount); } else { ReorderRows(true); } } ListViewDataColumns& DataProvider::GetDataColumns() { return dataColumns; } DataColumns& DataProvider::GetColumns() { return columns; } Ptr DataProvider::GetItemSource() { return itemSource; } void DataProvider::SetItemSource(Ptr _itemSource) { vint oldCount = 0; if (itemSource) { oldCount = itemSource->GetCount(); } if (itemChangedEventHandler) { auto ol = itemSource.Cast(); ol->ItemChanged.Remove(itemChangedEventHandler); } itemSource = nullptr; itemChangedEventHandler = nullptr; if (_itemSource) { if (auto ol = _itemSource.Cast()) { itemSource = ol; itemChangedEventHandler = ol->ItemChanged.Add([this](vint start, vint oldCount, vint newCount) { OnItemSourceModified(start, oldCount, newCount); }); } else if (auto rl = _itemSource.Cast()) { itemSource = rl; } else { itemSource = IValueList::Create(GetLazyList(_itemSource)); } } OnItemSourceModified(0, oldCount, itemSource ? itemSource->GetCount() : 0); } void DataProvider::RebuildFilter() { if (currentFilter) { currentFilter->SetCallback(nullptr); currentFilter = nullptr; } List> selectedFilters; CopyFrom( selectedFilters, From(columns) .Select([](Ptr column) {return column->GetFilter(); }) .Where([](Ptr filter) {return filter != nullptr; }) ); if (additionalFilter) { selectedFilters.Add(additionalFilter); } if (selectedFilters.Count() > 0) { auto andFilter = MakePtr(); FOREACH(Ptr, filter, selectedFilters) { andFilter->AddSubFilter(filter); } currentFilter = andFilter; } if (currentFilter) { currentFilter->SetCallback(this); } } void DataProvider::ReorderRows(bool invokeCallback) { vint oldRowCount = virtualRowToSourceRow.Count(); virtualRowToSourceRow.Clear(); vint rowCount = itemSource ? itemSource->GetCount() : 0; if (currentFilter) { for (vint i = 0; i < rowCount; i++) { if (currentFilter->Filter(itemSource->Get(i))) { virtualRowToSourceRow.Add(i); } } } else { for (vint i = 0; i < rowCount; i++) { virtualRowToSourceRow.Add(i); } } if (currentSorter && virtualRowToSourceRow.Count() > 0) { IDataSorter* sorter = currentSorter.Obj(); SortLambda( &virtualRowToSourceRow[0], virtualRowToSourceRow.Count(), [=](vint a, vint b) { return sorter->Compare(itemSource->Get(a), itemSource->Get(b)); }); } if (invokeCallback) { NotifyAllItemsUpdate(); } } DataProvider::DataProvider() :dataColumns(this) , columns(this) { RebuildFilter(); ReorderRows(false); } DataProvider::~DataProvider() { } Ptr DataProvider::GetAdditionalFilter() { return additionalFilter; } void DataProvider::SetAdditionalFilter(Ptr value) { additionalFilter = value; RebuildFilter(); ReorderRows(true); } // ===================== GuiListControl::IItemProvider ===================== vint DataProvider::Count() { return virtualRowToSourceRow.Count(); } WString DataProvider::GetTextValue(vint itemIndex) { return GetText(itemIndex); } description::Value DataProvider::GetBindingValue(vint itemIndex) { return itemSource ? itemSource->Get(virtualRowToSourceRow[itemIndex]) : Value(); } IDescriptable* DataProvider::RequestView(const WString& identifier) { if (identifier == IListViewItemView::Identifier) { return (IListViewItemView*)this; } else if (identifier == ListViewColumnItemArranger::IColumnItemView::Identifier) { return (ListViewColumnItemArranger::IColumnItemView*)this; } else if (identifier == IDataGridView::Identifier) { return (IDataGridView*)this; } else { return nullptr; } } // ===================== list::IListViewItemProvider ===================== Ptr DataProvider::GetSmallImage(vint itemIndex) { if (0 <= itemIndex && itemIndex < Count()) { return ReadProperty(GetBindingValue(itemIndex), smallImageProperty); } return nullptr; } Ptr DataProvider::GetLargeImage(vint itemIndex) { if (0 <= itemIndex && itemIndex < Count()) { return ReadProperty(GetBindingValue(itemIndex), largeImageProperty); } return nullptr; } WString DataProvider::GetText(vint itemIndex) { if (columns.Count() == 0)return L""; return columns[0]->GetCellText(itemIndex); } WString DataProvider::GetSubItem(vint itemIndex, vint index) { return columns[index + 1]->GetCellText(itemIndex); } vint DataProvider::GetDataColumnCount() { return dataColumns.Count(); } vint DataProvider::GetDataColumn(vint index) { return dataColumns[index]; } vint DataProvider::GetColumnCount() { return columns.Count(); } WString DataProvider::GetColumnText(vint index) { return columns[index]->GetText(); } // ===================== list::ListViewColumnItemArranger::IColumnItemView ===================== bool DataProvider::AttachCallback(ListViewColumnItemArranger::IColumnItemViewCallback* value) { if (columnItemViewCallback)return false; columnItemViewCallback = value; return true; } bool DataProvider::DetachCallback(ListViewColumnItemArranger::IColumnItemViewCallback* value) { if (!columnItemViewCallback) return false; columnItemViewCallback = nullptr; return true; } vint DataProvider::GetColumnSize(vint index) { return columns[index]->GetSize(); } void DataProvider::SetColumnSize(vint index, vint value) { columns[index]->SetSize(value); } GuiMenu* DataProvider::GetDropdownPopup(vint index) { return columns[index]->GetPopup(); } ColumnSortingState DataProvider::GetSortingState(vint index) { return columns[index]->sortingState; } // ===================== list::IDataGridView ===================== bool DataProvider::IsColumnSortable(vint column) { return columns[column]->GetSorter(); } void DataProvider::SortByColumn(vint column, bool ascending) { if (0 <= column && column < columns.Count()) { auto sorter = columns[column]->GetSorter(); if (!sorter) { currentSorter = nullptr; } else if (ascending) { currentSorter = sorter; } else { Ptr reverseSorter = new DataReverseSorter(); reverseSorter->SetSubSorter(sorter); currentSorter = reverseSorter; } } else { currentSorter = nullptr; } for (vint i = 0; i < columns.Count(); i++) { columns[i]->sortingState = i != column ? ColumnSortingState::NotSorted : ascending ? ColumnSortingState::Ascending : ColumnSortingState::Descending ; } NotifyAllColumnsUpdate(); ReorderRows(true); } vint DataProvider::GetSortedColumn() { for (vint i = 0; i < columns.Count(); i++) { auto state = columns[i]->sortingState; if (state != ColumnSortingState::NotSorted) { return i; } } return -1; } bool DataProvider::IsSortOrderAscending() { for (vint i = 0; i < columns.Count(); i++) { auto state = columns[i]->sortingState; if (state != ColumnSortingState::NotSorted) { return state == ColumnSortingState::Ascending; } } return true; } vint DataProvider::GetCellSpan(vint row, vint column) { return 1; } IDataVisualizerFactory* DataProvider::GetCellDataVisualizerFactory(vint row, vint column) { return columns[column]->GetVisualizerFactory().Obj(); } IDataEditorFactory* DataProvider::GetCellDataEditorFactory(vint row, vint column) { return columns[column]->GetEditorFactory().Obj(); } description::Value DataProvider::GetBindingCellValue(vint row, vint column) { return columns[column]->GetCellValue(row); } void DataProvider::SetBindingCellValue(vint row, vint column, const description::Value& value) { columns[column]->SetCellValue(row, value); } } /*********************************************************************** GuiBindableDataGrid ***********************************************************************/ GuiBindableDataGrid::GuiBindableDataGrid(theme::ThemeName themeName) :GuiVirtualDataGrid(themeName, new list::DataProvider) { dataProvider = dynamic_cast(GetItemProvider()); } GuiBindableDataGrid::~GuiBindableDataGrid() { } list::ListViewDataColumns& GuiBindableDataGrid::GetDataColumns() { return dataProvider->GetDataColumns(); } list::DataColumns& GuiBindableDataGrid::GetColumns() { return dataProvider->GetColumns(); } Ptr GuiBindableDataGrid::GetItemSource() { return dataProvider->GetItemSource(); } void GuiBindableDataGrid::SetItemSource(Ptr _itemSource) { dataProvider->SetItemSource(_itemSource); } Ptr GuiBindableDataGrid::GetAdditionalFilter() { return dataProvider->GetAdditionalFilter(); } void GuiBindableDataGrid::SetAdditionalFilter(Ptr value) { dataProvider->SetAdditionalFilter(value); } ItemProperty> GuiBindableDataGrid::GetLargeImageProperty() { return dataProvider->largeImageProperty; } void GuiBindableDataGrid::SetLargeImageProperty(const ItemProperty>& value) { if (dataProvider->largeImageProperty != value) { dataProvider->largeImageProperty = value; dataProvider->NotifyAllItemsUpdate(); LargeImagePropertyChanged.Execute(GetNotifyEventArguments()); } } ItemProperty> GuiBindableDataGrid::GetSmallImageProperty() { return dataProvider->smallImageProperty; } void GuiBindableDataGrid::SetSmallImageProperty(const ItemProperty>& value) { if (dataProvider->smallImageProperty != value) { dataProvider->smallImageProperty = value; dataProvider->NotifyAllItemsUpdate(); SmallImagePropertyChanged.Execute(GetNotifyEventArguments()); } } description::Value GuiBindableDataGrid::GetSelectedRowValue() { auto pos = GetSelectedCell(); if (pos.row == -1 || pos.column == -1) { return Value(); } return dataProvider->GetBindingValue(GetSelectedCell().row); } description::Value GuiBindableDataGrid::GetSelectedCellValue() { auto pos = GetSelectedCell(); if (pos.row == -1 || pos.column == -1) { return Value(); } return dataProvider->GetColumns()[pos.column]->GetCellValue(pos.row); } } } } /*********************************************************************** .\CONTROLS\LISTCONTROLPACKAGE\GUIBINDABLELISTCONTROLS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace collections; using namespace list; using namespace tree; using namespace reflection::description; using namespace templates; /*********************************************************************** GuiBindableTextList::ItemSource ***********************************************************************/ GuiBindableTextList::ItemSource::ItemSource() { } GuiBindableTextList::ItemSource::~ItemSource() { SetItemSource(nullptr); } Ptr GuiBindableTextList::ItemSource::GetItemSource() { return itemSource; } void GuiBindableTextList::ItemSource::SetItemSource(Ptr _itemSource) { vint oldCount = 0; if (itemSource) { oldCount = itemSource->GetCount(); } if (itemChangedEventHandler) { auto ol = itemSource.Cast(); ol->ItemChanged.Remove(itemChangedEventHandler); } itemSource = nullptr; itemChangedEventHandler = nullptr; if (_itemSource) { if (auto ol = _itemSource.Cast()) { itemSource = ol; itemChangedEventHandler = ol->ItemChanged.Add([this](vint start, vint oldCount, vint newCount) { InvokeOnItemModified(start, oldCount, newCount); }); } else if (auto rl = _itemSource.Cast()) { itemSource = rl; } else { itemSource = IValueList::Create(GetLazyList(_itemSource)); } } InvokeOnItemModified(0, oldCount, itemSource ? itemSource->GetCount() : 0); } description::Value GuiBindableTextList::ItemSource::Get(vint index) { if (!itemSource) return Value(); return itemSource->Get(index); } void GuiBindableTextList::ItemSource::UpdateBindingProperties() { InvokeOnItemModified(0, Count(), Count()); } // ===================== GuiListControl::IItemProvider ===================== vint GuiBindableTextList::ItemSource::Count() { if (!itemSource) return 0; return itemSource->GetCount(); } WString GuiBindableTextList::ItemSource::GetTextValue(vint itemIndex) { if (itemSource) { if (0 <= itemIndex && itemIndex < itemSource->GetCount()) { return ReadProperty(itemSource->Get(itemIndex), textProperty); } } return L""; } IDescriptable* GuiBindableTextList::ItemSource::RequestView(const WString& identifier) { if (identifier == ITextItemView::Identifier) { return (ITextItemView*)this; } else { return 0; } } // ===================== GuiListControl::IItemBindingView ===================== description::Value GuiBindableTextList::ItemSource::GetBindingValue(vint itemIndex) { if (itemSource) { if (0 <= itemIndex && itemIndex < itemSource->GetCount()) { return itemSource->Get(itemIndex); } } return Value(); } // ===================== list::TextItemStyleProvider::ITextItemView ===================== bool GuiBindableTextList::ItemSource::GetChecked(vint itemIndex) { if (itemSource) { if (0 <= itemIndex && itemIndex < itemSource->GetCount()) { return ReadProperty(itemSource->Get(itemIndex), checkedProperty); } } return false; } void GuiBindableTextList::ItemSource::SetChecked(vint itemIndex, bool value) { if (itemSource) { if (0 <= itemIndex && itemIndex < itemSource->GetCount()) { auto thisValue = itemSource->Get(itemIndex); WriteProperty(thisValue, checkedProperty, value); InvokeOnItemModified(itemIndex, 1, 1); } } } /*********************************************************************** GuiBindableTextList ***********************************************************************/ GuiBindableTextList::GuiBindableTextList(theme::ThemeName themeName) :GuiVirtualTextList(themeName, new ItemSource) { itemSource = dynamic_cast(GetItemProvider()); TextPropertyChanged.SetAssociatedComposition(boundsComposition); TextPropertyChanged.SetAssociatedComposition(boundsComposition); } GuiBindableTextList::~GuiBindableTextList() { } Ptr GuiBindableTextList::GetItemSource() { return itemSource->GetItemSource(); } void GuiBindableTextList::SetItemSource(Ptr _itemSource) { itemSource->SetItemSource(_itemSource); } ItemProperty GuiBindableTextList::GetTextProperty() { return itemSource->textProperty; } void GuiBindableTextList::SetTextProperty(const ItemProperty& value) { if (itemSource->textProperty != value) { itemSource->textProperty = value; itemSource->UpdateBindingProperties(); TextPropertyChanged.Execute(GetNotifyEventArguments()); } } WritableItemProperty GuiBindableTextList::GetCheckedProperty() { return itemSource->checkedProperty; } void GuiBindableTextList::SetCheckedProperty(const WritableItemProperty& value) { if (itemSource->checkedProperty != value) { itemSource->checkedProperty = value; itemSource->UpdateBindingProperties(); CheckedPropertyChanged.Execute(GetNotifyEventArguments()); } } description::Value GuiBindableTextList::GetSelectedItem() { vint index = GetSelectedItemIndex(); if (index == -1) return Value(); return itemSource->Get(index); } /*********************************************************************** GuiBindableListView::ItemSource ***********************************************************************/ GuiBindableListView::ItemSource::ItemSource() :columns(this) , dataColumns(this) { } GuiBindableListView::ItemSource::~ItemSource() { SetItemSource(nullptr); } Ptr GuiBindableListView::ItemSource::GetItemSource() { return itemSource; } void GuiBindableListView::ItemSource::SetItemSource(Ptr _itemSource) { vint oldCount = 0; if (itemSource) { oldCount = itemSource->GetCount(); } if (itemChangedEventHandler) { auto ol = itemSource.Cast(); ol->ItemChanged.Remove(itemChangedEventHandler); } itemSource = nullptr; itemChangedEventHandler = nullptr; if (_itemSource) { if (auto ol = _itemSource.Cast()) { itemSource = ol; itemChangedEventHandler = ol->ItemChanged.Add([this](vint start, vint oldCount, vint newCount) { InvokeOnItemModified(start, oldCount, newCount); }); } else if (auto rl = _itemSource.Cast()) { itemSource = rl; } else { itemSource = IValueList::Create(GetLazyList(_itemSource)); } } InvokeOnItemModified(0, oldCount, itemSource ? itemSource->GetCount() : 0); } description::Value GuiBindableListView::ItemSource::Get(vint index) { if (!itemSource) return Value(); return itemSource->Get(index); } void GuiBindableListView::ItemSource::UpdateBindingProperties() { InvokeOnItemModified(0, Count(), Count()); } bool GuiBindableListView::ItemSource::NotifyUpdate(vint start, vint count) { if (!itemSource) return false; if (start<0 || start >= itemSource->GetCount() || count <= 0 || start + count > itemSource->GetCount()) { return false; } else { InvokeOnItemModified(start, count, count); return true; } } list::ListViewDataColumns& GuiBindableListView::ItemSource::GetDataColumns() { return dataColumns; } list::ListViewColumns& GuiBindableListView::ItemSource::GetColumns() { return columns; } // ===================== list::IListViewItemProvider ===================== void GuiBindableListView::ItemSource::NotifyAllItemsUpdate() { NotifyUpdate(0, Count()); } void GuiBindableListView::ItemSource::NotifyAllColumnsUpdate() { for (vint i = 0; i < columnItemViewCallbacks.Count(); i++) { columnItemViewCallbacks[i]->OnColumnChanged(); } } // ===================== GuiListControl::IItemProvider ===================== vint GuiBindableListView::ItemSource::Count() { if (!itemSource) return 0; return itemSource->GetCount(); } WString GuiBindableListView::ItemSource::GetTextValue(vint itemIndex) { return GetText(itemIndex); } description::Value GuiBindableListView::ItemSource::GetBindingValue(vint itemIndex) { if (itemSource) { if (0 <= itemIndex && itemIndex < itemSource->GetCount()) { return itemSource->Get(itemIndex); } } return Value(); } IDescriptable* GuiBindableListView::ItemSource::RequestView(const WString& identifier) { if (identifier == IListViewItemView::Identifier) { return (IListViewItemView*)this; } else if (identifier == ListViewColumnItemArranger::IColumnItemView::Identifier) { return (ListViewColumnItemArranger::IColumnItemView*)this; } else { return 0; } } // ===================== list::ListViewItemStyleProvider::IListViewItemView ===================== Ptr GuiBindableListView::ItemSource::GetSmallImage(vint itemIndex) { if (itemSource) { if (0 <= itemIndex && itemIndex < itemSource->GetCount()) { return ReadProperty(itemSource->Get(itemIndex), smallImageProperty); } } return nullptr; } Ptr GuiBindableListView::ItemSource::GetLargeImage(vint itemIndex) { if (itemSource) { if (0 <= itemIndex && itemIndex < itemSource->GetCount()) { return ReadProperty(itemSource->Get(itemIndex), largeImageProperty); } } return nullptr; } WString GuiBindableListView::ItemSource::GetText(vint itemIndex) { if (itemSource) { if (0 <= itemIndex && itemIndex < itemSource->GetCount() && columns.Count()>0) { return ReadProperty(itemSource->Get(itemIndex), columns[0]->GetTextProperty()); } } return L""; } WString GuiBindableListView::ItemSource::GetSubItem(vint itemIndex, vint index) { if (itemSource) { if (0 <= itemIndex && itemIndex < itemSource->GetCount() && 0 <= index && index < columns.Count() - 1) { return ReadProperty(itemSource->Get(itemIndex), columns[index + 1]->GetTextProperty()); } } return L""; } vint GuiBindableListView::ItemSource::GetDataColumnCount() { return dataColumns.Count(); } vint GuiBindableListView::ItemSource::GetDataColumn(vint index) { return dataColumns[index]; } vint GuiBindableListView::ItemSource::GetColumnCount() { return columns.Count(); } WString GuiBindableListView::ItemSource::GetColumnText(vint index) { if (index < 0 || index >= columns.Count()) { return L""; } else { return columns[index]->GetText(); } } // ===================== list::ListViewColumnItemArranger::IColumnItemView ===================== bool GuiBindableListView::ItemSource::AttachCallback(ListViewColumnItemArranger::IColumnItemViewCallback* value) { if(columnItemViewCallbacks.Contains(value)) { return false; } else { columnItemViewCallbacks.Add(value); return true; } } bool GuiBindableListView::ItemSource::DetachCallback(ListViewColumnItemArranger::IColumnItemViewCallback* value) { vint index = columnItemViewCallbacks.IndexOf(value); if (index == -1) { return false; } else { columnItemViewCallbacks.Remove(value); return true; } } vint GuiBindableListView::ItemSource::GetColumnSize(vint index) { if (index < 0 || index >= columns.Count()) { return 0; } else { return columns[index]->GetSize(); } } void GuiBindableListView::ItemSource::SetColumnSize(vint index, vint value) { if (index >= 0 && index < columns.Count()) { columns[index]->SetSize(value); } } GuiMenu* GuiBindableListView::ItemSource::GetDropdownPopup(vint index) { if (index < 0 || index >= columns.Count()) { return 0; } else { return columns[index]->GetDropdownPopup(); } } ColumnSortingState GuiBindableListView::ItemSource::GetSortingState(vint index) { if (index < 0 || index >= columns.Count()) { return ColumnSortingState::NotSorted; } else { return columns[index]->GetSortingState(); } } /*********************************************************************** GuiBindableListView ***********************************************************************/ GuiBindableListView::GuiBindableListView(theme::ThemeName themeName) :GuiVirtualListView(themeName, new ItemSource) { itemSource = dynamic_cast(GetItemProvider()); LargeImagePropertyChanged.SetAssociatedComposition(boundsComposition); SmallImagePropertyChanged.SetAssociatedComposition(boundsComposition); } GuiBindableListView::~GuiBindableListView() { } list::ListViewDataColumns& GuiBindableListView::GetDataColumns() { return itemSource->GetDataColumns(); } list::ListViewColumns& GuiBindableListView::GetColumns() { return itemSource->GetColumns(); } Ptr GuiBindableListView::GetItemSource() { return itemSource->GetItemSource(); } void GuiBindableListView::SetItemSource(Ptr _itemSource) { itemSource->SetItemSource(_itemSource); } ItemProperty> GuiBindableListView::GetLargeImageProperty() { return itemSource->largeImageProperty; } void GuiBindableListView::SetLargeImageProperty(const ItemProperty>& value) { if (itemSource->largeImageProperty != value) { itemSource->largeImageProperty = value; itemSource->UpdateBindingProperties(); LargeImagePropertyChanged.Execute(GetNotifyEventArguments()); } } ItemProperty> GuiBindableListView::GetSmallImageProperty() { return itemSource->smallImageProperty; } void GuiBindableListView::SetSmallImageProperty(const ItemProperty>& value) { if (itemSource->smallImageProperty != value) { itemSource->smallImageProperty = value; itemSource->UpdateBindingProperties(); SmallImagePropertyChanged.Execute(GetNotifyEventArguments()); } } description::Value GuiBindableListView::GetSelectedItem() { vint index = GetSelectedItemIndex(); if (index == -1) return Value(); return itemSource->Get(index); } /*********************************************************************** GuiBindableTreeView::ItemSourceNode ***********************************************************************/ void GuiBindableTreeView::ItemSourceNode::PrepareChildren() { if (!childrenVirtualList) { if (auto value = ReadProperty(itemSource, rootProvider->childrenProperty)) { if (auto ol = value.Cast()) { itemChangedEventHandler = ol->ItemChanged.Add([this](vint start, vint oldCount, vint newCount) { callback->OnBeforeItemModified(this, start, oldCount, newCount); children.RemoveRange(start, oldCount); for (vint i = 0; i < newCount; i++) { Value value = childrenVirtualList->Get(start + i); auto node = new ItemSourceNode(value, this); children.Insert(start + i, node); } callback->OnAfterItemModified(this, start, oldCount, newCount); }); childrenVirtualList = ol; } else if (auto rl = value.Cast()) { childrenVirtualList = rl; } else { childrenVirtualList = IValueList::Create(GetLazyList(value)); } } if (!childrenVirtualList) { childrenVirtualList = IValueList::Create(); } vint count = childrenVirtualList->GetCount(); for (vint i = 0; i < count; i++) { Value value = childrenVirtualList->Get(i); auto node = new ItemSourceNode(value, this); children.Add(node); } } } void GuiBindableTreeView::ItemSourceNode::UnprepareChildren() { if (itemChangedEventHandler) { auto ol = childrenVirtualList.Cast(); ol->ItemChanged.Remove(itemChangedEventHandler); itemChangedEventHandler = nullptr; } childrenVirtualList = nullptr; FOREACH(Ptr, node, children) { node->UnprepareChildren(); } children.Clear(); } GuiBindableTreeView::ItemSourceNode::ItemSourceNode(const description::Value& _itemSource, ItemSourceNode* _parent) :itemSource(_itemSource) , rootProvider(_parent->rootProvider) , parent(_parent) , callback(_parent->callback) { } GuiBindableTreeView::ItemSourceNode::ItemSourceNode(ItemSource* _rootProvider) :rootProvider(_rootProvider) , parent(nullptr) , callback(_rootProvider) { } GuiBindableTreeView::ItemSourceNode::~ItemSourceNode() { } description::Value GuiBindableTreeView::ItemSourceNode::GetItemSource() { return itemSource; } void GuiBindableTreeView::ItemSourceNode::SetItemSource(const description::Value& _itemSource) { vint oldCount = GetChildCount(); UnprepareChildren(); itemSource = _itemSource; vint newCount = GetChildCount(); callback->OnBeforeItemModified(this, 0, oldCount, newCount); callback->OnAfterItemModified(this, 0, oldCount, newCount); } bool GuiBindableTreeView::ItemSourceNode::GetExpanding() { return this == rootProvider->rootNode.Obj() ? true : expanding; } void GuiBindableTreeView::ItemSourceNode::SetExpanding(bool value) { if (this != rootProvider->rootNode.Obj() && expanding != value) { expanding = value; if (expanding) { callback->OnItemExpanded(this); } else { callback->OnItemCollapsed(this); } } } vint GuiBindableTreeView::ItemSourceNode::CalculateTotalVisibleNodes() { if (!GetExpanding()) { return 1; } PrepareChildren(); vint count = 1; FOREACH(Ptr, child, children) { count += child->CalculateTotalVisibleNodes(); } return count; } vint GuiBindableTreeView::ItemSourceNode::GetChildCount() { PrepareChildren(); return children.Count(); } Ptr GuiBindableTreeView::ItemSourceNode::GetParent() { return parent; } Ptr GuiBindableTreeView::ItemSourceNode::GetChild(vint index) { PrepareChildren(); if (0 <= index && index < children.Count()) { return children[index]; } return nullptr; } /*********************************************************************** GuiBindableTreeView::ItemSource ***********************************************************************/ GuiBindableTreeView::ItemSource::ItemSource() { rootNode = new ItemSourceNode(this); } GuiBindableTreeView::ItemSource::~ItemSource() { } description::Value GuiBindableTreeView::ItemSource::GetItemSource() { return rootNode->GetItemSource(); } void GuiBindableTreeView::ItemSource::SetItemSource(const description::Value& _itemSource) { rootNode->SetItemSource(_itemSource); } void GuiBindableTreeView::ItemSource::UpdateBindingProperties(bool updateChildrenProperty) { vint oldCount = rootNode->GetChildCount(); if (updateChildrenProperty) { rootNode->UnprepareChildren(); } vint newCount = rootNode->GetChildCount(); OnBeforeItemModified(rootNode.Obj(), 0, oldCount, newCount); OnAfterItemModified(rootNode.Obj(), 0, oldCount, newCount); } // ===================== tree::INodeRootProvider ===================== Ptr GuiBindableTreeView::ItemSource::GetRootNode() { return rootNode; } WString GuiBindableTreeView::ItemSource::GetTextValue(tree::INodeProvider* node) { return ReadProperty(GetBindingValue(node), textProperty); } description::Value GuiBindableTreeView::ItemSource::GetBindingValue(tree::INodeProvider* node) { if (auto itemSourceNode = dynamic_cast(node)) { return itemSourceNode->GetItemSource(); } return Value(); } IDescriptable* GuiBindableTreeView::ItemSource::RequestView(const WString& identifier) { if(identifier==ITreeViewItemView::Identifier) { return (ITreeViewItemView*)this; } else { return 0; } } // ===================== tree::ITreeViewItemView ===================== Ptr GuiBindableTreeView::ItemSource::GetNodeImage(tree::INodeProvider* node) { if (auto itemSourceNode = dynamic_cast(node)) { return ReadProperty(itemSourceNode->GetItemSource(), imageProperty); } return nullptr; } /*********************************************************************** GuiBindableTreeView ***********************************************************************/ GuiBindableTreeView::GuiBindableTreeView(theme::ThemeName themeName) :GuiVirtualTreeView(themeName, new ItemSource) { itemSource = dynamic_cast(GetNodeRootProvider()); TextPropertyChanged.SetAssociatedComposition(boundsComposition); ImagePropertyChanged.SetAssociatedComposition(boundsComposition); ChildrenPropertyChanged.SetAssociatedComposition(boundsComposition); } GuiBindableTreeView::~GuiBindableTreeView() { } description::Value GuiBindableTreeView::GetItemSource() { return itemSource->GetItemSource(); } void GuiBindableTreeView::SetItemSource(description::Value _itemSource) { itemSource->SetItemSource(_itemSource); } ItemProperty GuiBindableTreeView::GetTextProperty() { return itemSource->textProperty; } void GuiBindableTreeView::SetTextProperty(const ItemProperty& value) { if (itemSource->textProperty != value) { itemSource->textProperty = value; itemSource->UpdateBindingProperties(false); TextPropertyChanged.Execute(GetNotifyEventArguments()); } } ItemProperty> GuiBindableTreeView::GetImageProperty() { return itemSource->imageProperty; } void GuiBindableTreeView::SetImageProperty(const ItemProperty>& value) { if (itemSource->imageProperty != value) { itemSource->imageProperty = value; itemSource->UpdateBindingProperties(false); ImagePropertyChanged.Execute(GetNotifyEventArguments()); } } ItemProperty> GuiBindableTreeView::GetChildrenProperty() { return itemSource->childrenProperty; } void GuiBindableTreeView::SetChildrenProperty(const ItemProperty>& value) { if (itemSource->childrenProperty != value) { itemSource->childrenProperty = value; itemSource->UpdateBindingProperties(true); ChildrenPropertyChanged.Execute(GetNotifyEventArguments()); } } description::Value GuiBindableTreeView::GetSelectedItem() { vint index = GetSelectedItemIndex(); if (index == -1) return Value(); Value result; if (auto node = nodeItemView->RequestNode(index)) { if (auto itemSourceNode = node.Cast()) { result = itemSourceNode->GetItemSource(); } } return result; } } } } /*********************************************************************** .\CONTROLS\LISTCONTROLPACKAGE\GUILISTVIEWCONTROLS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace elements; using namespace compositions; using namespace collections; using namespace reflection::description; /*********************************************************************** GuiListViewColumnHeader ***********************************************************************/ void GuiListViewColumnHeader::BeforeControlTemplateUninstalled_() { } void GuiListViewColumnHeader::AfterControlTemplateInstalled_(bool initialize) { GetControlTemplateObject(true)->SetSortingState(columnSortingState); } GuiListViewColumnHeader::GuiListViewColumnHeader(theme::ThemeName themeName) :GuiMenuButton(themeName) { } GuiListViewColumnHeader::~GuiListViewColumnHeader() { } bool GuiListViewColumnHeader::IsAltAvailable() { return false; } ColumnSortingState GuiListViewColumnHeader::GetColumnSortingState() { return columnSortingState; } void GuiListViewColumnHeader::SetColumnSortingState(ColumnSortingState value) { if (columnSortingState != value) { columnSortingState = value; GetControlTemplateObject(true)->SetSortingState(columnSortingState); } } /*********************************************************************** GuiListViewBase ***********************************************************************/ void GuiListViewBase::BeforeControlTemplateUninstalled_() { } void GuiListViewBase::AfterControlTemplateInstalled_(bool initialize) { } GuiListViewBase::GuiListViewBase(theme::ThemeName themeName, GuiListControl::IItemProvider* _itemProvider) :GuiSelectableListControl(themeName, _itemProvider) { ColumnClicked.SetAssociatedComposition(boundsComposition); } GuiListViewBase::~GuiListViewBase() { } namespace list { const wchar_t* const IListViewItemView::Identifier = L"vl::presentation::controls::list::IListViewItemView"; /*********************************************************************** ListViewColumnItemArranger::ColumnItemViewCallback ***********************************************************************/ ListViewColumnItemArranger::ColumnItemViewCallback::ColumnItemViewCallback(ListViewColumnItemArranger* _arranger) :arranger(_arranger) { } ListViewColumnItemArranger::ColumnItemViewCallback::~ColumnItemViewCallback() { } void ListViewColumnItemArranger::ColumnItemViewCallback::OnColumnChanged() { arranger->RebuildColumns(); FOREACH(ItemStyleRecord, style, arranger->visibleStyles) { if (auto callback = dynamic_cast(style.key)) { callback->OnColumnChanged(); } } } /*********************************************************************** ListViewColumnItemArranger ***********************************************************************/ const wchar_t* const ListViewColumnItemArranger::IColumnItemView::Identifier = L"vl::presentation::controls::list::ListViewColumnItemArranger::IColumnItemView"; void ListViewColumnItemArranger::ColumnClicked(vint index, compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { GuiItemEventArgs args(listView->ColumnClicked.GetAssociatedComposition()); args.itemIndex=index; listView->ColumnClicked.Execute(args); } void ListViewColumnItemArranger::ColumnBoundsChanged(vint index, compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { GuiBoundsComposition* buttonBounds=columnHeaderButtons[index]->GetBoundsComposition(); vint size=buttonBounds->GetBounds().Width(); if(size>columnItemView->GetColumnSize(index)) { columnItemView->SetColumnSize(index, size); } } void ListViewColumnItemArranger::ColumnHeaderSplitterLeftButtonDown(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { if(listView->GetVisuallyEnabled()) { arguments.handled=true; splitterDragging=true; splitterLatestX=arguments.x; } } void ListViewColumnItemArranger::ColumnHeaderSplitterLeftButtonUp(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { if(listView->GetVisuallyEnabled()) { arguments.handled=true; splitterDragging=false; splitterLatestX=0; } } void ListViewColumnItemArranger::ColumnHeaderSplitterMouseMove(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { if(splitterDragging) { vint offset=arguments.x-splitterLatestX; vint index=columnHeaderSplitters.IndexOf(dynamic_cast(sender)); if(index!=-1) { GuiBoundsComposition* buttonBounds=columnHeaderButtons[index]->GetBoundsComposition(); Rect bounds=buttonBounds->GetBounds(); Rect newBounds(bounds.LeftTop(), Size(bounds.Width()+offset, bounds.Height())); buttonBounds->SetBounds(newBounds); vint finalSize=buttonBounds->GetBounds().Width(); columnItemView->SetColumnSize(index, finalSize); } } } void ListViewColumnItemArranger::RearrangeItemBounds() { FixedHeightItemArranger::RearrangeItemBounds(); vint count = columnHeaders->GetParent()->Children().Count(); columnHeaders->GetParent()->MoveChild(columnHeaders, count - 1); columnHeaders->SetBounds(Rect(Point(-viewBounds.Left(), 0), Size(0, 0))); } vint ListViewColumnItemArranger::GetWidth() { vint width=columnHeaders->GetBounds().Width()-SplitterWidth; if(widthGetBounds().Height(); } Size ListViewColumnItemArranger::OnCalculateTotalSize() { Size size=FixedHeightItemArranger::OnCalculateTotalSize(); size.x+=SplitterWidth; return size; } void ListViewColumnItemArranger::DeleteColumnButtons() { for(vint i=columnHeaders->GetStackItems().Count()-1;i>=0;i--) { GuiStackItemComposition* item=columnHeaders->GetStackItems().Get(i); columnHeaders->RemoveChild(item); GuiControl* button=item->Children().Get(0)->GetAssociatedControl(); if(button) { item->RemoveChild(button->GetBoundsComposition()); delete button; } delete item; } columnHeaderButtons.Clear(); columnHeaderSplitters.Clear(); } void ListViewColumnItemArranger::RebuildColumns() { if (columnItemView && columnHeaderButtons.Count() == listViewItemView->GetColumnCount()) { for (vint i = 0; i < listViewItemView->GetColumnCount(); i++) { GuiListViewColumnHeader* button = columnHeaderButtons[i]; button->SetText(listViewItemView->GetColumnText(i)); button->SetSubMenu(columnItemView->GetDropdownPopup(i), false); button->SetColumnSortingState(columnItemView->GetSortingState(i)); button->GetBoundsComposition()->SetBounds(Rect(Point(0, 0), Size(columnItemView->GetColumnSize(i), 0))); } } else { DeleteColumnButtons(); if (columnItemView && listViewItemView) { for (vint i = 0; i < listViewItemView->GetColumnCount(); i++) { GuiBoundsComposition* splitterComposition = new GuiBoundsComposition; splitterComposition->SetAlignmentToParent(Margin(0, 0, 0, 0)); splitterComposition->SetAssociatedCursor(GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::SizeWE)); splitterComposition->SetAlignmentToParent(Margin(0, 0, -1, 0)); splitterComposition->SetPreferredMinSize(Size(SplitterWidth, 0)); columnHeaderSplitters.Add(splitterComposition); splitterComposition->GetEventReceiver()->leftButtonDown.AttachMethod(this, &ListViewColumnItemArranger::ColumnHeaderSplitterLeftButtonDown); splitterComposition->GetEventReceiver()->leftButtonUp.AttachMethod(this, &ListViewColumnItemArranger::ColumnHeaderSplitterLeftButtonUp); splitterComposition->GetEventReceiver()->mouseMove.AttachMethod(this, &ListViewColumnItemArranger::ColumnHeaderSplitterMouseMove); } for (vint i = 0; i < listViewItemView->GetColumnCount(); i++) { GuiListViewColumnHeader* button = new GuiListViewColumnHeader(theme::ThemeName::Unknown); button->SetControlTemplate(listView->GetControlTemplateObject(true)->GetColumnHeaderTemplate()); button->SetText(listViewItemView->GetColumnText(i)); button->SetSubMenu(columnItemView->GetDropdownPopup(i), false); button->SetColumnSortingState(columnItemView->GetSortingState(i)); button->GetBoundsComposition()->SetBounds(Rect(Point(0, 0), Size(columnItemView->GetColumnSize(i), 0))); button->Clicked.AttachLambda(Curry(Func(this, &ListViewColumnItemArranger::ColumnClicked))(i)); button->GetBoundsComposition()->BoundsChanged.AttachLambda(Curry(Func(this, &ListViewColumnItemArranger::ColumnBoundsChanged))(i)); columnHeaderButtons.Add(button); if (i > 0) { button->GetContainerComposition()->AddChild(columnHeaderSplitters[i - 1]); } GuiStackItemComposition* item = new GuiStackItemComposition; item->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); item->AddChild(button->GetBoundsComposition()); columnHeaders->AddChild(item); } if (listViewItemView->GetColumnCount() > 0) { GuiBoundsComposition* splitterComposition = columnHeaderSplitters[listViewItemView->GetColumnCount() - 1]; GuiStackItemComposition* item = new GuiStackItemComposition; item->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); item->AddChild(splitterComposition); columnHeaders->AddChild(item); } } } callback->OnTotalSizeChanged(); } ListViewColumnItemArranger::ListViewColumnItemArranger() { columnHeaders = new GuiStackComposition; columnHeaders->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); columnItemViewCallback = new ColumnItemViewCallback(this); } ListViewColumnItemArranger::~ListViewColumnItemArranger() { if(!columnHeaders->GetParent()) { DeleteColumnButtons(); delete columnHeaders; } } void ListViewColumnItemArranger::AttachListControl(GuiListControl* value) { FixedHeightItemArranger::AttachListControl(value); listView = dynamic_cast(value); if (listView) { listViewItemView = dynamic_cast(listView->GetItemProvider()->RequestView(IListViewItemView::Identifier)); columnItemView = dynamic_cast(listView->GetItemProvider()->RequestView(IColumnItemView::Identifier)); listView->GetContainerComposition()->AddChild(columnHeaders); if (columnItemView) { columnItemView->AttachCallback(columnItemViewCallback.Obj()); RebuildColumns(); } } } void ListViewColumnItemArranger::DetachListControl() { if (listView) { if (columnItemView) { columnItemView->DetachCallback(columnItemViewCallback.Obj()); columnItemView = nullptr; } listViewItemView = nullptr; listView->GetContainerComposition()->RemoveChild(columnHeaders); listView = nullptr; } FixedHeightItemArranger::DetachListControl(); } /*********************************************************************** ListViewSubItems ***********************************************************************/ void ListViewSubItems::NotifyUpdateInternal(vint start, vint count, vint newCount) { owner->NotifyUpdate(); } /*********************************************************************** ListViewItem ***********************************************************************/ void ListViewItem::NotifyUpdate() { if (owner) { vint index = owner->IndexOf(this); owner->NotifyUpdateInternal(index, 1, 1); } } ListViewItem::ListViewItem() :owner(0) { subItems.owner = this; } ListViewSubItems& ListViewItem::GetSubItems() { return subItems; } Ptr ListViewItem::GetSmallImage() { return smallImage; } void ListViewItem::SetSmallImage(Ptr value) { smallImage = value; NotifyUpdate(); } Ptr ListViewItem::GetLargeImage() { return largeImage; } void ListViewItem::SetLargeImage(Ptr value) { largeImage = value; NotifyUpdate(); } const WString& ListViewItem::GetText() { return text; } void ListViewItem::SetText(const WString& value) { text = value; NotifyUpdate(); } description::Value ListViewItem::GetTag() { return tag; } void ListViewItem::SetTag(const description::Value& value) { tag = value; NotifyUpdate(); } /*********************************************************************** ListViewColumn ***********************************************************************/ void ListViewColumn::NotifyUpdate(bool affectItem) { if (owner) { vint index = owner->IndexOf(this); owner->NotifyColumnUpdated(index, affectItem); } } ListViewColumn::ListViewColumn(const WString& _text, vint _size) :text(_text) ,size(_size) { } ListViewColumn::~ListViewColumn() { if (dropdownPopup && ownPopup) { SafeDeleteControl(dropdownPopup); } } const WString& ListViewColumn::GetText() { return text; } void ListViewColumn::SetText(const WString& value) { if (text != value) { text = value; NotifyUpdate(false); } } ItemProperty ListViewColumn::GetTextProperty() { return textProperty; } void ListViewColumn::SetTextProperty(const ItemProperty& value) { textProperty = value; NotifyUpdate(true); } vint ListViewColumn::GetSize() { return size; } void ListViewColumn::SetSize(vint value) { if (size != value) { size = value; NotifyUpdate(false); } } bool ListViewColumn::GetOwnPopup() { return ownPopup; } void ListViewColumn::SetOwnPopup(bool value) { ownPopup = value; } GuiMenu* ListViewColumn::GetDropdownPopup() { return dropdownPopup; } void ListViewColumn::SetDropdownPopup(GuiMenu* value) { if (dropdownPopup != value) { dropdownPopup = value; NotifyUpdate(false); } } ColumnSortingState ListViewColumn::GetSortingState() { return sortingState; } void ListViewColumn::SetSortingState(ColumnSortingState value) { if (sortingState != value) { sortingState = value; NotifyUpdate(false); } } /*********************************************************************** ListViewDataColumns ***********************************************************************/ void ListViewDataColumns::NotifyUpdateInternal(vint start, vint count, vint newCount) { itemProvider->NotifyAllItemsUpdate(); } ListViewDataColumns::ListViewDataColumns(IListViewItemProvider* _itemProvider) :itemProvider(_itemProvider) { } ListViewDataColumns::~ListViewDataColumns() { } /*********************************************************************** ListViewColumns ***********************************************************************/ void ListViewColumns::NotifyColumnUpdated(vint column, bool affectItem) { affectItemFlag = affectItem; NotifyUpdate(column, 1); affectItemFlag = true; } void ListViewColumns::AfterInsert(vint index, const Ptr& value) { collections::ObservableListBase>::AfterInsert(index, value); value->owner = this; } void ListViewColumns::BeforeRemove(vint index, const Ptr& value) { value->owner = 0; collections::ObservableListBase>::BeforeRemove(index, value); } void ListViewColumns::NotifyUpdateInternal(vint start, vint count, vint newCount) { itemProvider->NotifyAllColumnsUpdate(); if (affectItemFlag) { itemProvider->NotifyAllItemsUpdate(); } } ListViewColumns::ListViewColumns(IListViewItemProvider* _itemProvider) :itemProvider(_itemProvider) { } ListViewColumns::~ListViewColumns() { } /*********************************************************************** ListViewItemProvider ***********************************************************************/ void ListViewItemProvider::AfterInsert(vint index, const Ptr& value) { ListProvider>::AfterInsert(index, value); value->owner = this; } void ListViewItemProvider::BeforeRemove(vint index, const Ptr& value) { value->owner = 0; ListProvider>::AfterInsert(index, value); } void ListViewItemProvider::NotifyAllItemsUpdate() { NotifyUpdate(0, Count()); } void ListViewItemProvider::NotifyAllColumnsUpdate() { for (vint i = 0; i < columnItemViewCallbacks.Count(); i++) { columnItemViewCallbacks[i]->OnColumnChanged(); } } Ptr ListViewItemProvider::GetSmallImage(vint itemIndex) { return Get(itemIndex)->smallImage; } Ptr ListViewItemProvider::GetLargeImage(vint itemIndex) { return Get(itemIndex)->largeImage; } WString ListViewItemProvider::GetText(vint itemIndex) { return Get(itemIndex)->text; } WString ListViewItemProvider::GetSubItem(vint itemIndex, vint index) { Ptr item=Get(itemIndex); if(index<0 || index>=item->GetSubItems().Count()) { return L""; } else { return item->GetSubItems()[index]; } } vint ListViewItemProvider::GetDataColumnCount() { return dataColumns.Count(); } vint ListViewItemProvider::GetDataColumn(vint index) { return dataColumns[index]; } vint ListViewItemProvider::GetColumnCount() { return columns.Count(); } WString ListViewItemProvider::GetColumnText(vint index) { if (index<0 || index >= columns.Count()) { return L""; } else { return columns[index]->GetText(); } } bool ListViewItemProvider::AttachCallback(ListViewColumnItemArranger::IColumnItemViewCallback* value) { if(columnItemViewCallbacks.Contains(value)) { return false; } else { columnItemViewCallbacks.Add(value); return true; } } bool ListViewItemProvider::DetachCallback(ListViewColumnItemArranger::IColumnItemViewCallback* value) { vint index=columnItemViewCallbacks.IndexOf(value); if(index==-1) { return false; } else { columnItemViewCallbacks.Remove(value); return true; } } vint ListViewItemProvider::GetColumnSize(vint index) { if(index<0 || index>=columns.Count()) { return 0; } else { return columns[index]->GetSize(); } } void ListViewItemProvider::SetColumnSize(vint index, vint value) { if(index>=0 && indexSetSize(value); } } GuiMenu* ListViewItemProvider::GetDropdownPopup(vint index) { if(index<0 || index>=columns.Count()) { return 0; } else { return columns[index]->GetDropdownPopup(); } } ColumnSortingState ListViewItemProvider::GetSortingState(vint index) { if (index < 0 || index >= columns.Count()) { return ColumnSortingState::NotSorted; } else { return columns[index]->GetSortingState(); } } WString ListViewItemProvider::GetTextValue(vint itemIndex) { return GetText(itemIndex); } description::Value ListViewItemProvider::GetBindingValue(vint itemIndex) { return Value::From(Get(itemIndex)); } ListViewItemProvider::ListViewItemProvider() :columns(this) , dataColumns(this) { } ListViewItemProvider::~ListViewItemProvider() { } IDescriptable* ListViewItemProvider::RequestView(const WString& identifier) { if (identifier == IListViewItemView::Identifier) { return (IListViewItemView*)this; } else if (identifier == ListViewColumnItemArranger::IColumnItemView::Identifier) { return (ListViewColumnItemArranger::IColumnItemView*)this; } else { return 0; } } ListViewDataColumns& ListViewItemProvider::GetDataColumns() { return dataColumns; } ListViewColumns& ListViewItemProvider::GetColumns() { return columns; } } /*********************************************************************** GuiListView ***********************************************************************/ void GuiVirtualListView::OnStyleInstalled(vint itemIndex, ItemStyle* style) { GuiListViewBase::OnStyleInstalled(itemIndex, style); } void GuiVirtualListView::OnItemTemplateChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { view = ListViewView::Unknown; } GuiVirtualListView::GuiVirtualListView(theme::ThemeName themeName, GuiListControl::IItemProvider* _itemProvider) :GuiListViewBase(themeName, _itemProvider) { SetView(ListViewView::Detail); } GuiVirtualListView::~GuiVirtualListView() { } ListViewView GuiVirtualListView::GetView() { return view; } void GuiVirtualListView::SetView(ListViewView _view) { switch (_view) { case ListViewView::BigIcon: SetStyleAndArranger( [](const Value&) { return new list::BigIconListViewItemTemplate; }, new list::FixedSizeMultiColumnItemArranger ); break; case ListViewView::SmallIcon: SetStyleAndArranger( [](const Value&) { return new list::SmallIconListViewItemTemplate; }, new list::FixedSizeMultiColumnItemArranger ); break; case ListViewView::List: SetStyleAndArranger( [](const Value&) { return new list::ListListViewItemTemplate; }, new list::FixedHeightMultiColumnItemArranger ); break; case ListViewView::Tile: SetStyleAndArranger( [](const Value&) { return new list::TileListViewItemTemplate; }, new list::FixedSizeMultiColumnItemArranger ); break; case ListViewView::Information: SetStyleAndArranger( [](const Value&) { return new list::InformationListViewItemTemplate; }, new list::FixedHeightItemArranger ); break; case ListViewView::Detail: SetStyleAndArranger( [](const Value&) { return new list::DetailListViewItemTemplate; }, new list::ListViewColumnItemArranger ); break; default:; } view = _view; } /*********************************************************************** GuiListView ***********************************************************************/ GuiListView::GuiListView(theme::ThemeName themeName) :GuiVirtualListView(themeName, new list::ListViewItemProvider) { items=dynamic_cast(itemProvider.Obj()); } GuiListView::~GuiListView() { } list::ListViewItemProvider& GuiListView::GetItems() { return *items; } list::ListViewDataColumns& GuiListView::GetDataColumns() { return items->GetDataColumns(); } list::ListViewColumns& GuiListView::GetColumns() { return items->GetColumns(); } Ptr GuiListView::GetSelectedItem() { vint index = GetSelectedItemIndex(); if (index == -1) return 0; return items->Get(index); } } } } /*********************************************************************** .\CONTROLS\LISTCONTROLPACKAGE\GUILISTVIEWITEMTEMPLATES.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace elements; using namespace compositions; using namespace collections; using namespace reflection::description; namespace list { /*********************************************************************** DefaultListViewItemTemplate ***********************************************************************/ DefaultListViewItemTemplate::DefaultListViewItemTemplate() { SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); } DefaultListViewItemTemplate::~DefaultListViewItemTemplate() { } /*********************************************************************** BigIconListViewItemTemplate ***********************************************************************/ void BigIconListViewItemTemplate::OnInitialize() { DefaultListViewItemTemplate::OnInitialize(); { auto table = new GuiTableComposition; AddChild(table); table->SetRowsAndColumns(2, 3); table->SetRowOption(0, GuiCellOption::MinSizeOption()); table->SetRowOption(1, GuiCellOption::MinSizeOption()); table->SetColumnOption(0, GuiCellOption::PercentageOption(0.5)); table->SetColumnOption(1, GuiCellOption::MinSizeOption()); table->SetColumnOption(2, GuiCellOption::PercentageOption(0.5)); table->SetAlignmentToParent(Margin(0, 0, 0, 0)); table->SetCellPadding(5); { auto cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(0, 1, 1, 1); cell->SetPreferredMinSize(Size(32, 32)); image = GuiImageFrameElement::Create(); image->SetStretch(true); cell->SetOwnedElement(image); } { auto cell = new GuiCellComposition; table->AddChild(cell); cell->SetMinSizeLimitation(GuiGraphicsComposition::NoLimit); cell->SetSite(1, 0, 1, 3); cell->SetPreferredMinSize(Size(64, 40)); text = GuiSolidLabelElement::Create(); text->SetAlignments(Alignment::Center, Alignment::Top); text->SetWrapLine(true); text->SetEllipse(true); cell->SetOwnedElement(text); } } if (auto listView = dynamic_cast(listControl)) { auto itemIndex = GetIndex(); if (auto view = dynamic_cast(listView->GetItemProvider()->RequestView(IListViewItemView::Identifier))) { auto imageData = view->GetLargeImage(itemIndex); if (imageData) { image->SetImage(imageData->GetImage(), imageData->GetFrameIndex()); } else { image->SetImage(nullptr); } text->SetText(view->GetText(itemIndex)); text->SetColor(listView->GetControlTemplateObject(true)->GetPrimaryTextColor()); } } FontChanged.AttachMethod(this, &BigIconListViewItemTemplate::OnFontChanged); FontChanged.Execute(compositions::GuiEventArgs(this)); } void BigIconListViewItemTemplate::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { text->SetFont(GetFont()); } BigIconListViewItemTemplate::BigIconListViewItemTemplate() { } BigIconListViewItemTemplate::~BigIconListViewItemTemplate() { } /*********************************************************************** SmallIconListViewItemTemplate ***********************************************************************/ void SmallIconListViewItemTemplate::OnInitialize() { DefaultListViewItemTemplate::OnInitialize(); { auto table = new GuiTableComposition; AddChild(table); table->SetRowsAndColumns(3, 2); table->SetRowOption(0, GuiCellOption::PercentageOption(0.5)); table->SetRowOption(1, GuiCellOption::MinSizeOption()); table->SetRowOption(2, GuiCellOption::PercentageOption(0.5)); table->SetColumnOption(0, GuiCellOption::MinSizeOption()); table->SetColumnOption(1, GuiCellOption::MinSizeOption()); table->SetAlignmentToParent(Margin(0, 0, 0, 0)); table->SetCellPadding(2); { GuiCellComposition* cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(1, 0, 1, 1); cell->SetPreferredMinSize(Size(16, 16)); image = GuiImageFrameElement::Create(); image->SetStretch(true); cell->SetOwnedElement(image); } { GuiCellComposition* cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(0, 1, 3, 1); cell->SetPreferredMinSize(Size(192, 0)); text = GuiSolidLabelElement::Create(); text->SetAlignments(Alignment::Left, Alignment::Center); text->SetEllipse(true); cell->SetOwnedElement(text); } } if (auto listView = dynamic_cast(listControl)) { auto itemIndex = GetIndex(); if (auto view = dynamic_cast(listView->GetItemProvider()->RequestView(IListViewItemView::Identifier))) { auto imageData = view->GetSmallImage(itemIndex); if (imageData) { image->SetImage(imageData->GetImage(), imageData->GetFrameIndex()); } else { image->SetImage(nullptr); } text->SetText(view->GetText(itemIndex)); text->SetColor(listView->GetControlTemplateObject(true)->GetPrimaryTextColor()); } } FontChanged.AttachMethod(this, &SmallIconListViewItemTemplate::OnFontChanged); FontChanged.Execute(compositions::GuiEventArgs(this)); } void SmallIconListViewItemTemplate::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { text->SetFont(GetFont()); } SmallIconListViewItemTemplate::SmallIconListViewItemTemplate() { } SmallIconListViewItemTemplate::~SmallIconListViewItemTemplate() { } /*********************************************************************** ListListViewItemTemplate ***********************************************************************/ void ListListViewItemTemplate::OnInitialize() { DefaultListViewItemTemplate::OnInitialize(); { auto table = new GuiTableComposition; AddChild(table); table->SetRowsAndColumns(3, 2); table->SetRowOption(0, GuiCellOption::PercentageOption(0.5)); table->SetRowOption(1, GuiCellOption::MinSizeOption()); table->SetRowOption(2, GuiCellOption::PercentageOption(0.5)); table->SetColumnOption(0, GuiCellOption::MinSizeOption()); table->SetColumnOption(1, GuiCellOption::MinSizeOption()); table->SetAlignmentToParent(Margin(0, 0, 0, 0)); table->SetCellPadding(2); { auto cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(1, 0, 1, 1); cell->SetPreferredMinSize(Size(16, 16)); image = GuiImageFrameElement::Create(); image->SetStretch(true); cell->SetOwnedElement(image); } { auto cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(0, 1, 3, 1); cell->SetMargin(Margin(0, 0, 16, 0)); text = GuiSolidLabelElement::Create(); text->SetAlignments(Alignment::Left, Alignment::Center); cell->SetOwnedElement(text); } } if (auto listView = dynamic_cast(listControl)) { auto itemIndex = GetIndex(); if (auto view = dynamic_cast(listView->GetItemProvider()->RequestView(IListViewItemView::Identifier))) { auto imageData = view->GetSmallImage(itemIndex); if (imageData) { image->SetImage(imageData->GetImage(), imageData->GetFrameIndex()); } else { image->SetImage(nullptr); } text->SetText(view->GetText(itemIndex)); text->SetColor(listView->GetControlTemplateObject(true)->GetPrimaryTextColor()); } } FontChanged.AttachMethod(this, &ListListViewItemTemplate::OnFontChanged); FontChanged.Execute(compositions::GuiEventArgs(this)); } void ListListViewItemTemplate::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { text->SetFont(GetFont()); } ListListViewItemTemplate::ListListViewItemTemplate() { } ListListViewItemTemplate::~ListListViewItemTemplate() { } /*********************************************************************** TileListViewItemTemplate ***********************************************************************/ elements::GuiSolidLabelElement* TileListViewItemTemplate::CreateTextElement(vint textRow) { auto cell = new GuiCellComposition; textTable->AddChild(cell); cell->SetSite(textRow + 1, 0, 1, 1); auto textElement = GuiSolidLabelElement::Create(); textElement->SetAlignments(Alignment::Left, Alignment::Center); textElement->SetEllipse(true); cell->SetOwnedElement(textElement); return textElement; } void TileListViewItemTemplate::ResetTextTable(vint textRows) { textTable->SetRowsAndColumns(textRows + 2, 1); textTable->SetRowOption(0, GuiCellOption::PercentageOption(0.5)); for (vint i = 0; iSetRowOption(i + 1, GuiCellOption::MinSizeOption()); } textTable->SetRowOption(textRows + 1, GuiCellOption::PercentageOption(0.5)); textTable->SetColumnOption(0, GuiCellOption::PercentageOption(1.0)); } void TileListViewItemTemplate::OnInitialize() { DefaultListViewItemTemplate::OnInitialize(); { auto table = new GuiTableComposition; AddChild(table); table->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); table->SetRowsAndColumns(3, 2); table->SetRowOption(0, GuiCellOption::PercentageOption(0.5)); table->SetRowOption(1, GuiCellOption::MinSizeOption()); table->SetRowOption(2, GuiCellOption::PercentageOption(0.5)); table->SetColumnOption(0, GuiCellOption::MinSizeOption()); table->SetColumnOption(1, GuiCellOption::MinSizeOption()); table->SetAlignmentToParent(Margin(0, 0, 0, 0)); table->SetCellPadding(4); { auto cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(1, 0, 1, 1); cell->SetPreferredMinSize(Size(32, 32)); image = GuiImageFrameElement::Create(); image->SetStretch(true); cell->SetOwnedElement(image); } { auto cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(0, 1, 3, 1); cell->SetPreferredMinSize(Size(224, 0)); textTable = new GuiTableComposition; textTable->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); textTable->SetCellPadding(1); ResetTextTable(1); textTable->SetAlignmentToParent(Margin(0, 0, 0, 0)); cell->AddChild(textTable); { text = CreateTextElement(0); } } } if (auto listView = dynamic_cast(listControl)) { auto itemIndex = GetIndex(); if (auto view = dynamic_cast(listView->GetItemProvider()->RequestView(IListViewItemView::Identifier))) { auto imageData = view->GetLargeImage(itemIndex); if (imageData) { image->SetImage(imageData->GetImage(), imageData->GetFrameIndex()); } else { image->SetImage(nullptr); } text->SetText(view->GetText(itemIndex)); text->SetColor(listView->GetControlTemplateObject(true)->GetPrimaryTextColor()); vint dataColumnCount = view->GetDataColumnCount(); ResetTextTable(dataColumnCount + 1); dataTexts.Resize(dataColumnCount); for (vint i = 0; i < dataColumnCount; i++) { dataTexts[i] = CreateTextElement(i + 1); dataTexts[i]->SetText(view->GetSubItem(itemIndex, view->GetDataColumn(i))); dataTexts[i]->SetColor(listView->GetControlTemplateObject(true)->GetSecondaryTextColor()); } } } FontChanged.AttachMethod(this, &TileListViewItemTemplate::OnFontChanged); FontChanged.Execute(compositions::GuiEventArgs(this)); } void TileListViewItemTemplate::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { text->SetFont(GetFont()); if (auto view = dynamic_cast(listControl->GetItemProvider()->RequestView(IListViewItemView::Identifier))) { vint dataColumnCount = view->GetDataColumnCount(); for (vint i = 0; i < dataColumnCount; i++) { dataTexts[i]->SetFont(GetFont()); } } } TileListViewItemTemplate::TileListViewItemTemplate() { } TileListViewItemTemplate::~TileListViewItemTemplate() { } /*********************************************************************** InformationListViewItemTemplate ***********************************************************************/ void InformationListViewItemTemplate::OnInitialize() { DefaultListViewItemTemplate::OnInitialize(); { bottomLine = GuiSolidBackgroundElement::Create(); bottomLineComposition = new GuiBoundsComposition; bottomLineComposition->SetOwnedElement(bottomLine); bottomLineComposition->SetAlignmentToParent(Margin(8, -1, 8, 0)); bottomLineComposition->SetPreferredMinSize(Size(0, 1)); AddChild(bottomLineComposition); auto table = new GuiTableComposition; AddChild(table); table->SetRowsAndColumns(3, 3); table->SetRowOption(0, GuiCellOption::PercentageOption(0.5)); table->SetRowOption(1, GuiCellOption::MinSizeOption()); table->SetRowOption(2, GuiCellOption::PercentageOption(0.5)); table->SetColumnOption(0, GuiCellOption::MinSizeOption()); table->SetColumnOption(1, GuiCellOption::PercentageOption(1.0)); table->SetColumnOption(2, GuiCellOption::MinSizeOption()); table->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); table->SetAlignmentToParent(Margin(0, 0, 0, 0)); table->SetCellPadding(4); { auto cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(1, 0, 1, 1); cell->SetPreferredMinSize(Size(32, 32)); image = GuiImageFrameElement::Create(); image->SetStretch(true); cell->SetOwnedElement(image); } { auto cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(0, 1, 3, 1); text = GuiSolidLabelElement::Create(); text->SetEllipse(true); cell->SetOwnedElement(text); } { auto cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(0, 2, 3, 1); cell->SetPreferredMinSize(Size(224, 0)); textTable = new GuiTableComposition; textTable->SetCellPadding(4); textTable->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); textTable->SetAlignmentToParent(Margin(0, 0, 0, 0)); cell->AddChild(textTable); } } if (auto listView = dynamic_cast(listControl)) { auto itemIndex = GetIndex(); if (auto view = dynamic_cast(listView->GetItemProvider()->RequestView(IListViewItemView::Identifier))) { auto imageData = view->GetLargeImage(itemIndex); if (imageData) { image->SetImage(imageData->GetImage(), imageData->GetFrameIndex()); } else { image->SetImage(nullptr); } text->SetText(view->GetText(itemIndex)); text->SetColor(listView->GetControlTemplateObject(true)->GetPrimaryTextColor()); bottomLine->SetColor(listView->GetControlTemplateObject(true)->GetItemSeparatorColor()); vint dataColumnCount = view->GetDataColumnCount(); columnTexts.Resize(dataColumnCount); dataTexts.Resize(dataColumnCount); textTable->SetRowsAndColumns(dataColumnCount + 2, 1); textTable->SetRowOption(0, GuiCellOption::PercentageOption(0.5)); for (vint i = 0; i < dataColumnCount; i++) { textTable->SetRowOption(i + 1, GuiCellOption::MinSizeOption()); } textTable->SetRowOption(dataColumnCount + 1, GuiCellOption::PercentageOption(0.5)); textTable->SetColumnOption(0, GuiCellOption::PercentageOption(1.0)); for (vint i = 0; i < dataColumnCount; i++) { auto cell = new GuiCellComposition; textTable->AddChild(cell); cell->SetSite(i + 1, 0, 1, 1); auto dataTable = new GuiTableComposition; dataTable->SetRowsAndColumns(1, 2); dataTable->SetRowOption(0, GuiCellOption::MinSizeOption()); dataTable->SetColumnOption(0, GuiCellOption::MinSizeOption()); dataTable->SetColumnOption(1, GuiCellOption::PercentageOption(1.0)); dataTable->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); dataTable->SetAlignmentToParent(Margin(0, 0, 0, 0)); cell->AddChild(dataTable); { auto cell = new GuiCellComposition; dataTable->AddChild(cell); cell->SetSite(0, 0, 1, 1); columnTexts[i] = GuiSolidLabelElement::Create(); columnTexts[i]->SetText(view->GetColumnText(view->GetDataColumn(i) + 1) + L": "); columnTexts[i]->SetColor(listView->GetControlTemplateObject(true)->GetSecondaryTextColor()); cell->SetOwnedElement(columnTexts[i]); } { auto cell = new GuiCellComposition; dataTable->AddChild(cell); cell->SetSite(0, 1, 1, 1); dataTexts[i]= GuiSolidLabelElement::Create(); dataTexts[i]->SetEllipse(true); dataTexts[i]->SetText(view->GetSubItem(itemIndex, view->GetDataColumn(i))); dataTexts[i]->SetColor(listView->GetControlTemplateObject(true)->GetPrimaryTextColor()); cell->SetOwnedElement(dataTexts[i]); } } } } FontChanged.AttachMethod(this, &InformationListViewItemTemplate::OnFontChanged); FontChanged.Execute(compositions::GuiEventArgs(this)); } void InformationListViewItemTemplate::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { { auto font = GetFont(); font.size = (vint)(font.size * 1.2); text->SetFont(font); } if (auto view = dynamic_cast(listControl->GetItemProvider()->RequestView(IListViewItemView::Identifier))) { vint dataColumnCount = view->GetDataColumnCount(); for (vint i = 0; i < dataColumnCount; i++) { columnTexts[i]->SetFont(GetFont()); dataTexts[i]->SetFont(GetFont()); } } } InformationListViewItemTemplate::InformationListViewItemTemplate() { } InformationListViewItemTemplate::~InformationListViewItemTemplate() { } /*********************************************************************** DetailListViewItemTemplate ***********************************************************************/ void DetailListViewItemTemplate::OnInitialize() { DefaultListViewItemTemplate::OnInitialize(); columnItemView = dynamic_cast(listControl->GetItemProvider()->RequestView(ListViewColumnItemArranger::IColumnItemView::Identifier)); { textTable = new GuiTableComposition; textTable->SetAlignmentToParent(Margin(0, 0, 0, 0)); textTable->SetRowsAndColumns(1, 1); textTable->SetRowOption(0, GuiCellOption::MinSizeOption()); textTable->SetColumnOption(0, GuiCellOption::AbsoluteOption(0)); AddChild(textTable); { auto cell = new GuiCellComposition; textTable->AddChild(cell); cell->SetSite(0, 0, 1, 1); auto table = new GuiTableComposition; cell->AddChild(table); table->SetRowsAndColumns(3, 2); table->SetRowOption(0, GuiCellOption::PercentageOption(0.5)); table->SetRowOption(1, GuiCellOption::MinSizeOption()); table->SetRowOption(2, GuiCellOption::PercentageOption(0.5)); table->SetColumnOption(0, GuiCellOption::MinSizeOption()); table->SetColumnOption(1, GuiCellOption::PercentageOption(1.0)); table->SetAlignmentToParent(Margin(0, 0, 0, 0)); table->SetCellPadding(2); { auto cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(1, 0, 1, 1); cell->SetPreferredMinSize(Size(16, 16)); image = GuiImageFrameElement::Create(); image->SetStretch(true); cell->SetOwnedElement(image); } { auto cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(0, 1, 3, 1); cell->SetMargin(Margin(0, 0, 8, 0)); text = GuiSolidLabelElement::Create(); text->SetAlignments(Alignment::Left, Alignment::Center); text->SetEllipse(true); cell->SetOwnedElement(text); } } } if (auto listView = dynamic_cast(listControl)) { auto itemIndex = GetIndex(); if (auto view = dynamic_cast(listView->GetItemProvider()->RequestView(IListViewItemView::Identifier))) { auto imageData = view->GetSmallImage(itemIndex); if (imageData) { image->SetImage(imageData->GetImage(), imageData->GetFrameIndex()); } else { image->SetImage(0); } text->SetText(view->GetText(itemIndex)); text->SetColor(listView->GetControlTemplateObject(true)->GetPrimaryTextColor()); vint columnCount = view->GetColumnCount() - 1; subItems.Resize(columnCount); textTable->SetRowsAndColumns(1, columnCount + 1); for (vint i = 0; i < columnCount; i++) { auto cell = new GuiCellComposition; textTable->AddChild(cell); cell->SetSite(0, i + 1, 1, 1); cell->SetMargin(Margin(8, 0, 8, 0)); subItems[i] = GuiSolidLabelElement::Create(); subItems[i]->SetAlignments(Alignment::Left, Alignment::Center); subItems[i]->SetFont(text->GetFont()); subItems[i]->SetEllipse(true); subItems[i]->SetText(view->GetSubItem(itemIndex, i)); subItems[i]->SetColor(listView->GetControlTemplateObject(true)->GetSecondaryTextColor()); cell->SetOwnedElement(subItems[i]); } OnColumnChanged(); } } FontChanged.AttachMethod(this, &DetailListViewItemTemplate::OnFontChanged); FontChanged.Execute(compositions::GuiEventArgs(this)); } void DetailListViewItemTemplate::OnColumnChanged() { if (auto view = dynamic_cast(listControl->GetItemProvider()->RequestView(IListViewItemView::Identifier))) { if (columnItemView) { vint columnCount = view->GetColumnCount(); if (columnCount>textTable->GetColumns()) { columnCount = textTable->GetColumns(); } for (vint i = 0; iSetColumnOption(i, GuiCellOption::AbsoluteOption(columnItemView->GetColumnSize(i))); } textTable->UpdateCellBounds(); } } } void DetailListViewItemTemplate::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { text->SetFont(GetFont()); if (auto view = dynamic_cast(listControl->GetItemProvider()->RequestView(IListViewItemView::Identifier))) { vint columnCount = view->GetColumnCount() - 1; for (vint i = 0; i < columnCount; i++) { subItems[i]->SetFont(GetFont()); } } } DetailListViewItemTemplate::DetailListViewItemTemplate() { } DetailListViewItemTemplate::~DetailListViewItemTemplate() { } } } } } /*********************************************************************** .\CONTROLS\TEXTEDITORPACKAGE\EDITORCALLBACK\GUITEXTGENERALOPERATIONS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { } } } /*********************************************************************** .\CONTROLS\TEXTEDITORPACKAGE\EDITORCALLBACK\GUITEXTCOLORIZER.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace elements; using namespace elements::text; /*********************************************************************** GuiTextBoxColorizerBase ***********************************************************************/ void GuiTextBoxColorizerBase::ColorizerThreadProc(void* argument) { GuiTextBoxColorizerBase* colorizer=(GuiTextBoxColorizerBase*)argument; while(!colorizer->isFinalizing) { vint lineIndex=-1; wchar_t* text=0; vuint32_t* colors=0; vint length=0; vint lexerState=-1; vint contextState=-1; SPIN_LOCK(*colorizer->elementModifyLock) { if(colorizer->colorizedLineCount>=colorizer->element->GetLines().GetCount()) { colorizer->isColorizerRunning=false; goto CANCEL_COLORIZING; } lineIndex=colorizer->colorizedLineCount++; TextLine& line=colorizer->element->GetLines().GetLine(lineIndex); length=line.dataLength; text=new wchar_t[length+2]; colors=new vuint32_t[length+2]; memcpy(text, line.text, sizeof(wchar_t)*length); text[length]=L'\r'; text[length+1]=L'\n'; lexerState=lineIndex==0?colorizer->GetLexerStartState():colorizer->element->GetLines().GetLine(lineIndex-1).lexerFinalState; contextState=lineIndex==0?colorizer->GetContextStartState():colorizer->element->GetLines().GetLine(lineIndex-1).contextFinalState; } colorizer->ColorizeLineWithCRLF(lineIndex, text, colors, length+2, lexerState, contextState); SPIN_LOCK(*colorizer->elementModifyLock) { if(lineIndexcolorizedLineCount && lineIndexelement->GetLines().GetCount()) { TextLine& line=colorizer->element->GetLines().GetLine(lineIndex); line.lexerFinalState=lexerState; line.contextFinalState=contextState; for(vint i=0;icolorizerRunningEvent.Leave(); } void GuiTextBoxColorizerBase::StartColorizer() { if(!isColorizerRunning) { isColorizerRunning=true; colorizerRunningEvent.Enter(); ThreadPoolLite::Queue(&GuiTextBoxColorizerBase::ColorizerThreadProc, this); } } void GuiTextBoxColorizerBase::StopColorizer(bool forever) { isFinalizing=true; colorizerRunningEvent.Enter(); colorizerRunningEvent.Leave(); colorizedLineCount=0; if(!forever) { isFinalizing=false; } } void GuiTextBoxColorizerBase::StopColorizerForever() { StopColorizer(true); } GuiTextBoxColorizerBase::GuiTextBoxColorizerBase() :element(0) ,elementModifyLock(0) ,colorizedLineCount(0) ,isColorizerRunning(false) ,isFinalizing(false) { } GuiTextBoxColorizerBase::~GuiTextBoxColorizerBase() { StopColorizerForever(); } void GuiTextBoxColorizerBase::Attach(elements::GuiColorizedTextElement* _element, SpinLock& _elementModifyLock, compositions::GuiGraphicsComposition* _ownerComposition, vuint editVersion) { if(_element) { SPIN_LOCK(_elementModifyLock) { element=_element; elementModifyLock=&_elementModifyLock; StartColorizer(); } } } void GuiTextBoxColorizerBase::Detach() { if(element && elementModifyLock) { StopColorizer(false); SPIN_LOCK(*elementModifyLock) { element=0; elementModifyLock=0; } } } void GuiTextBoxColorizerBase::TextEditPreview(TextEditPreviewStruct& arguments) { } void GuiTextBoxColorizerBase::TextEditNotify(const TextEditNotifyStruct& arguments) { if(element && elementModifyLock) { SPIN_LOCK(*elementModifyLock) { vint line =arguments.originalStart.rowline) { colorizedLineCount=line; } StartColorizer(); } } } void GuiTextBoxColorizerBase::TextCaretChanged(const TextCaretChangedStruct& arguments) { } void GuiTextBoxColorizerBase::TextEditFinished(vuint editVersion) { } void GuiTextBoxColorizerBase::RestartColorizer() { if(element && elementModifyLock) { SPIN_LOCK(*elementModifyLock) { colorizedLineCount=0; StartColorizer(); } } } /*********************************************************************** GuiTextBoxRegexColorizer ***********************************************************************/ struct GuiTextBoxRegexColorizerProcData { GuiTextBoxRegexColorizer* colorizer; vint lineIndex; const wchar_t* text; vuint32_t* colors; vint contextState; }; void GuiTextBoxRegexColorizer::ColorizerProc(void* argument, vint start, vint length, vint token) { GuiTextBoxRegexColorizerProcData& data=*(GuiTextBoxRegexColorizerProcData*)argument; data.colorizer->ColorizeTokenContextSensitive(data.lineIndex, data.text, start, length, token, data.contextState); for(vint i=0;i& GuiTextBoxRegexColorizer::GetTokenRegexes() { return tokenRegexes; } collections::List& GuiTextBoxRegexColorizer::GetTokenColors() { return tokenColors; } collections::List& GuiTextBoxRegexColorizer::GetExtraTokenColors() { return extraTokenColors; } vint GuiTextBoxRegexColorizer::GetExtraTokenIndexStart() { if(lexer) { return tokenColors.Count(); } else { return -1; } } bool GuiTextBoxRegexColorizer::SetDefaultColor(elements::text::ColorEntry value) { if(lexer) { return false; } else { defaultColor=value; return true; } } vint GuiTextBoxRegexColorizer::AddToken(const WString& regex, elements::text::ColorEntry color) { if(lexer) { return -1; } else { tokenRegexes.Add(regex); tokenColors.Add(color); return tokenColors.Count()-1; } } vint GuiTextBoxRegexColorizer::AddExtraToken(elements::text::ColorEntry color) { if(lexer) { return -1; } else { extraTokenColors.Add(color); return extraTokenColors.Count()-1; } } void GuiTextBoxRegexColorizer::ClearTokens() { tokenRegexes.Clear(); tokenColors.Clear(); extraTokenColors.Clear(); lexer=0; } void GuiTextBoxRegexColorizer::Setup() { if(lexer || tokenRegexes.Count()==0) { colors.Resize(1); colors[0]=defaultColor; } else { lexer=new regex::RegexLexer(tokenRegexes); colors.Resize(1+tokenRegexes.Count()+extraTokenColors.Count()); colors[0]=defaultColor; for(vint i=0;iColorize()); } } void GuiTextBoxRegexColorizer::ColorizeTokenContextSensitive(vint lineIndex, const wchar_t* text, vint start, vint length, vint& token, vint& contextState) { } vint GuiTextBoxRegexColorizer::GetLexerStartState() { return lexer?colorizer->GetStartState():-1; } vint GuiTextBoxRegexColorizer::GetContextStartState() { return 0; } void GuiTextBoxRegexColorizer::ColorizeLineWithCRLF(vint lineIndex, const wchar_t* text, vuint32_t* colors, vint length, vint& lexerState, vint& contextState) { memset(colors, 0, sizeof(*colors)*length); if(lexer) { GuiTextBoxRegexColorizerProcData data; data.colorizer=this; data.lineIndex=lineIndex; data.text=text; data.colors=colors; data.contextState=contextState; colorizer->Reset(lexerState); colorizer->Colorize(text, length, &GuiTextBoxRegexColorizer::ColorizerProc, &data); lexerState=colorizer->GetCurrentState(); contextState=data.contextState; } else { lexerState=-1; contextState=-1; } } const GuiTextBoxRegexColorizer::ColorArray& GuiTextBoxRegexColorizer::GetColors() { return colors; } } } } /*********************************************************************** .\CONTROLS\LISTCONTROLPACKAGE\GUIDATAGRIDCONTROLS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { namespace list { using namespace compositions; using namespace collections; using namespace description; using namespace templates; const wchar_t* const IDataGridView::Identifier = L"vl::presentation::controls::list::IDataGridView"; /*********************************************************************** DefaultDataGridItemTemplate ***********************************************************************/ IDataVisualizerFactory* DefaultDataGridItemTemplate::GetDataVisualizerFactory(vint row, vint column) { if (auto dataGrid = dynamic_cast(listControl)) { if (auto factory = dataGrid->dataGridView->GetCellDataVisualizerFactory(row, column)) { return factory; } if (column == 0) { return dataGrid->defaultMainColumnVisualizerFactory.Obj(); } else { return dataGrid->defaultSubColumnVisualizerFactory.Obj(); } } return nullptr; } IDataEditorFactory* DefaultDataGridItemTemplate::GetDataEditorFactory(vint row, vint column) { if (auto dataGrid = dynamic_cast(listControl)) { return dataGrid->dataGridView->GetCellDataEditorFactory(row, column); } return nullptr; } vint DefaultDataGridItemTemplate::GetCellColumnIndex(compositions::GuiGraphicsComposition* composition) { for (vint i = 0; i < textTable->GetColumns(); i++) { auto cell = textTable->GetSitedCell(0, i); if (composition == cell) { return i; } } return -1; } void DefaultDataGridItemTemplate::OnCellButtonUp(compositions::GuiGraphicsComposition* sender, bool openEditor) { if (auto dataGrid = dynamic_cast(listControl)) { vint index = GetCellColumnIndex(sender); if (index != -1) { if (currentEditor && dataGrid->GetSelectedCell().column == index) { return; } vint currentRow = GetIndex(); dataGrid->StartEdit(currentRow, index); } } } bool DefaultDataGridItemTemplate::IsInEditor(compositions::GuiMouseEventArgs& arguments) { if (auto dataGrid = dynamic_cast(listControl)) { if (!dataGrid->currentEditor) return false; auto editorComposition = dataGrid->currentEditor->GetTemplate(); auto currentComposition = arguments.eventSource; while (currentComposition) { if (currentComposition == editorComposition) { arguments.handled = true; return true; } else if (currentComposition == this) { break; } else { currentComposition = currentComposition->GetParent(); } } } return false; } void DefaultDataGridItemTemplate::OnCellButtonDown(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { IsInEditor(arguments); } void DefaultDataGridItemTemplate::OnCellLeftButtonUp(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { if (auto dataGrid = dynamic_cast(listControl)) { if (!IsInEditor(arguments)) { if (dataGrid->GetVisuallyEnabled()) { OnCellButtonUp(sender, true); } } } } void DefaultDataGridItemTemplate::OnCellRightButtonUp(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { if (auto dataGrid = dynamic_cast(listControl)) { if (!IsInEditor(arguments)) { if (dataGrid->GetVisuallyEnabled()) { OnCellButtonUp(sender, false); } } } } void DefaultDataGridItemTemplate::OnColumnChanged() { UpdateSubItemSize(); } void DefaultDataGridItemTemplate::OnInitialize() { DefaultListViewItemTemplate::OnInitialize(); { textTable = new GuiTableComposition; textTable->SetAlignmentToParent(Margin(0, 0, 0, 0)); textTable->SetRowsAndColumns(1, 1); textTable->SetRowOption(0, GuiCellOption::MinSizeOption()); textTable->SetColumnOption(0, GuiCellOption::AbsoluteOption(0)); AddChild(textTable); } if (auto dataGrid = dynamic_cast(listControl)) { vint columnCount = dataGrid->listViewItemView->GetColumnCount(); vint itemIndex = GetIndex(); dataVisualizers.Resize(columnCount); for (vint i = 0; i < dataVisualizers.Count(); i++) { auto factory = GetDataVisualizerFactory(itemIndex, i); dataVisualizers[i] = factory->CreateVisualizer(dataGrid); } textTable->SetRowsAndColumns(1, columnCount); for (vint i = 0; i < columnCount; i++) { auto cell = new GuiCellComposition; textTable->AddChild(cell); cell->SetSite(0, i, 1, 1); cell->GetEventReceiver()->leftButtonDown.AttachMethod(this, &DefaultDataGridItemTemplate::OnCellButtonDown); cell->GetEventReceiver()->rightButtonDown.AttachMethod(this, &DefaultDataGridItemTemplate::OnCellButtonDown); cell->GetEventReceiver()->leftButtonUp.AttachMethod(this, &DefaultDataGridItemTemplate::OnCellLeftButtonUp); cell->GetEventReceiver()->rightButtonUp.AttachMethod(this, &DefaultDataGridItemTemplate::OnCellRightButtonUp); auto composition = dataVisualizers[i]->GetTemplate(); composition->SetAlignmentToParent(Margin(0, 0, 0, 0)); cell->AddChild(composition); } for (vint i = 0; i < dataVisualizers.Count(); i++) { dataVisualizers[i]->BeforeVisualizeCell(dataGrid->GetItemProvider(), itemIndex, i); } GridPos selectedCell = dataGrid->GetSelectedCell(); if (selectedCell.row == itemIndex) { NotifySelectCell(selectedCell.column); } else { NotifySelectCell(-1); } UpdateSubItemSize(); } SelectedChanged.AttachMethod(this, &DefaultDataGridItemTemplate::OnSelectedChanged); FontChanged.AttachMethod(this, &DefaultDataGridItemTemplate::OnFontChanged); ContextChanged.AttachMethod(this, &DefaultDataGridItemTemplate::OnContextChanged); SelectedChanged.Execute(compositions::GuiEventArgs(this)); FontChanged.Execute(compositions::GuiEventArgs(this)); ContextChanged.Execute(compositions::GuiEventArgs(this)); } void DefaultDataGridItemTemplate::OnSelectedChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if (!GetSelected()) { NotifySelectCell(-1); } } void DefaultDataGridItemTemplate::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { FOREACH(Ptr, visualizer, dataVisualizers) { visualizer->GetTemplate()->SetFont(GetFont()); } if (currentEditor) { currentEditor->GetTemplate()->SetFont(GetFont()); } } void DefaultDataGridItemTemplate::OnContextChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { FOREACH(Ptr, visualizer, dataVisualizers) { visualizer->GetTemplate()->SetContext(GetContext()); } if (currentEditor) { currentEditor->GetTemplate()->SetContext(GetContext()); } } DefaultDataGridItemTemplate::DefaultDataGridItemTemplate() { } DefaultDataGridItemTemplate::~DefaultDataGridItemTemplate() { FOREACH(Ptr, visualizer, dataVisualizers) { visualizer->NotifyDeletedTemplate(); } if (currentEditor) { currentEditor->NotifyDeletedTemplate(); } } void DefaultDataGridItemTemplate::UpdateSubItemSize() { if (auto dataGrid = dynamic_cast(listControl)) { vint columnCount = dataGrid->listViewItemView->GetColumnCount(); if (columnCount > textTable->GetColumns()) { columnCount = textTable->GetColumns(); } for (vint i = 0; i < columnCount; i++) { textTable->SetColumnOption(i, GuiCellOption::AbsoluteOption(dataGrid->columnItemView->GetColumnSize(i))); } textTable->UpdateCellBounds(); } } bool DefaultDataGridItemTemplate::IsEditorOpened() { return currentEditor != nullptr; } void DefaultDataGridItemTemplate::NotifyOpenEditor(vint column, IDataEditor* editor) { currentEditor = editor; if (currentEditor) { auto cell = textTable->GetSitedCell(0, column); auto* editorBounds = currentEditor->GetTemplate(); editorBounds->SetFont(GetFont()); editorBounds->SetContext(GetContext()); if (editorBounds->GetParent() && editorBounds->GetParent() != cell) { editorBounds->GetParent()->RemoveChild(editorBounds); } editorBounds->SetAlignmentToParent(Margin(0, 0, 0, 0)); cell->AddChild(editorBounds); if (auto focusControl = currentEditor->GetTemplate()->GetFocusControl()) { focusControl->SetFocus(); } } } void DefaultDataGridItemTemplate::NotifyCloseEditor() { if (currentEditor) { auto composition = currentEditor->GetTemplate(); if (composition->GetParent()) { composition->GetParent()->RemoveChild(composition); } currentEditor = nullptr; } } void DefaultDataGridItemTemplate::NotifySelectCell(vint column) { for (vint i = 0; i < dataVisualizers.Count(); i++) { dataVisualizers[i]->SetSelected(i == column); } } void DefaultDataGridItemTemplate::NotifyCellEdited() { for (vint i = 0; i < dataVisualizers.Count(); i++) { dataVisualizers[i]->BeforeVisualizeCell(listControl->GetItemProvider(), GetIndex(), i); } } } /*********************************************************************** GuiVirtualDataGrid (Editor) ***********************************************************************/ using namespace list; void GuiVirtualDataGrid::OnItemModified(vint start, vint count, vint newCount) { GuiVirtualListView::OnItemModified(start, count, newCount); if(!GetItemProvider()->IsEditing()) { StopEdit(false); } } void GuiVirtualDataGrid::OnStyleUninstalled(ItemStyle* style) { GuiVirtualListView::OnStyleUninstalled(style); if (auto itemStyle = dynamic_cast(style)) { if (itemStyle->IsEditorOpened()) { itemStyle->NotifyCloseEditor(); currentEditor = nullptr; currentEditorPos = { -1,-1 }; } } } void GuiVirtualDataGrid::NotifyCloseEditor() { if (currentEditorPos.row != -1 && GetArranger()) { auto style = GetArranger()->GetVisibleStyle(currentEditorPos.row); if (auto itemStyle = dynamic_cast(style)) { itemStyle->NotifyCloseEditor(); } } } void GuiVirtualDataGrid::NotifySelectCell(vint row, vint column) { selectedCell = { row, column }; SelectedCellChanged.Execute(GetNotifyEventArguments()); auto style = GetArranger()->GetVisibleStyle(row); if (auto itemStyle = dynamic_cast(style)) { itemStyle->NotifySelectCell(column); } } bool GuiVirtualDataGrid::StartEdit(vint row, vint column) { StopEdit(true); NotifySelectCell(row, column); auto style = GetArranger()->GetVisibleStyle(row); if (auto itemStyle = dynamic_cast(style)) { if (auto factory = dataGridView->GetCellDataEditorFactory(row, column)) { currentEditorOpeningEditor = true; currentEditorPos = { row,column }; currentEditor = factory->CreateEditor(this); currentEditor->BeforeEditCell(GetItemProvider(), row, column); itemStyle->NotifyOpenEditor(column, currentEditor.Obj()); currentEditorOpeningEditor = false; return true; } } return false; } void GuiVirtualDataGrid::StopEdit(bool forOpenNewEditor) { if (GetItemProvider()->IsEditing()) { NotifyCloseEditor(); } else { if (currentEditorPos != GridPos{-1, -1}) { if (currentEditor) { NotifyCloseEditor(); } if (!forOpenNewEditor) { NotifySelectCell(-1, -1); } } } currentEditor = nullptr; currentEditorPos = { -1,-1 }; } /*********************************************************************** GuiVirtualDataGrid (IDataGridContext) ***********************************************************************/ templates::GuiListViewTemplate* GuiVirtualDataGrid::GetListViewControlTemplate() { return GetControlTemplateObject(true); } void GuiVirtualDataGrid::RequestSaveData() { if (currentEditor && !currentEditorOpeningEditor) { GuiControl* focusedControl = nullptr; if (auto controlHost = GetRelatedControlHost()) { if (auto graphicsHost = controlHost->GetGraphicsHost()) { if (auto focusComposition = graphicsHost->GetFocusedComposition()) { focusedControl = focusComposition->GetRelatedControl(); } } } GetItemProvider()->PushEditing(); dataGridView->SetBindingCellValue(currentEditorPos.row, currentEditorPos.column, currentEditor->GetTemplate()->GetCellValue()); GetItemProvider()->PopEditing(); auto style = GetArranger()->GetVisibleStyle(currentEditorPos.row); if (auto itemStyle = dynamic_cast(style)) { itemStyle->NotifyCellEdited(); } if (currentEditor && focusedControl) { focusedControl->SetFocus(); } } } /*********************************************************************** GuiVirtualDataGrid ***********************************************************************/ void GuiVirtualDataGrid::OnColumnClicked(compositions::GuiGraphicsComposition* sender, compositions::GuiItemEventArgs& arguments) { if(dataGridView->IsColumnSortable(arguments.itemIndex)) { switch(columnItemView->GetSortingState(arguments.itemIndex)) { case ColumnSortingState::NotSorted: dataGridView->SortByColumn(arguments.itemIndex, true); break; case ColumnSortingState::Ascending: dataGridView->SortByColumn(arguments.itemIndex, false); break; case ColumnSortingState::Descending: dataGridView->SortByColumn(-1, false); break; } } } GuiVirtualDataGrid::GuiVirtualDataGrid(theme::ThemeName themeName, GuiListControl::IItemProvider* _itemProvider) :GuiVirtualListView(themeName, _itemProvider) { listViewItemView = dynamic_cast(_itemProvider->RequestView(IListViewItemView::Identifier)); columnItemView = dynamic_cast(_itemProvider->RequestView(ListViewColumnItemArranger::IColumnItemView::Identifier)); dataGridView = dynamic_cast(_itemProvider->RequestView(IDataGridView::Identifier)); { auto mainProperty = [](const Value&) { return new MainColumnVisualizerTemplate; }; auto subProperty = [](const Value&) { return new SubColumnVisualizerTemplate; }; auto cellBorderProperty = [](const Value&) { return new CellBorderVisualizerTemplate; }; auto mainFactory = MakePtr(mainProperty); auto subFactory = MakePtr(subProperty); defaultMainColumnVisualizerFactory = MakePtr(cellBorderProperty, mainFactory); defaultSubColumnVisualizerFactory = MakePtr(cellBorderProperty, subFactory); } CHECK_ERROR(listViewItemView != nullptr, L"GuiVirtualDataGrid::GuiVirtualDataGrid(IStyleController*, GuiListControl::IItemProvider*)#Missing IListViewItemView from item provider."); CHECK_ERROR(columnItemView != nullptr, L"GuiVirtualDataGrid::GuiVirtualDataGrid(IStyleController*, GuiListControl::IItemProvider*)#Missing ListViewColumnItemArranger::IColumnItemView from item provider."); CHECK_ERROR(dataGridView != nullptr, L"GuiVirtualDataGrid::GuiVirtualDataGrid(IStyleController*, GuiListControl::IItemProvider*)#Missing IDataGridView from item provider."); SetViewToDefault(); ColumnClicked.AttachMethod(this, &GuiVirtualDataGrid::OnColumnClicked); SelectedCellChanged.SetAssociatedComposition(boundsComposition); } GuiVirtualDataGrid::~GuiVirtualDataGrid() { } GuiListControl::IItemProvider* GuiVirtualDataGrid::GetItemProvider() { return GuiVirtualListView::GetItemProvider(); } void GuiVirtualDataGrid::SetViewToDefault() { SetStyleAndArranger( [](const Value&) { return new list::DefaultDataGridItemTemplate; }, new list::ListViewColumnItemArranger ); } GridPos GuiVirtualDataGrid::GetSelectedCell() { return selectedCell; } void GuiVirtualDataGrid::SetSelectedCell(const GridPos& value) { if (selectedCell == value) { return; } bool validPos = 0 <= value.row && value.row < GetItemProvider()->Count() && 0 <= value.column && value.column < listViewItemView->GetColumnCount(); StopEdit(true); if (validPos) { NotifySelectCell(value.row, value.column); } else { NotifySelectCell(-1, -1); } if (validPos) { EnsureItemVisible(value.row); ClearSelection(); SetSelected(value.row, true); StartEdit(value.row, value.column); } else { ClearSelection(); } } } } } /*********************************************************************** .\CONTROLS\LISTCONTROLPACKAGE\GUIDATAGRIDEXTENSIONS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { namespace list { using namespace compositions; using namespace elements; using namespace theme; using namespace templates; /*********************************************************************** DataVisualizerBase ***********************************************************************/ DataVisualizerBase::DataVisualizerBase() { } DataVisualizerBase::~DataVisualizerBase() { if (visualizerTemplate) { SafeDeleteComposition(visualizerTemplate); } } IDataVisualizerFactory* DataVisualizerBase::GetFactory() { return factory; } templates::GuiGridVisualizerTemplate* DataVisualizerBase::GetTemplate() { return visualizerTemplate; } void DataVisualizerBase::NotifyDeletedTemplate() { visualizerTemplate = nullptr; } void DataVisualizerBase::BeforeVisualizeCell(GuiListControl::IItemProvider* itemProvider, vint row, vint column) { if (auto listViewItemView = dynamic_cast(dataGridContext->GetItemProvider()->RequestView(IListViewItemView::Identifier))) { auto style = dataGridContext->GetListViewControlTemplate(); visualizerTemplate->SetPrimaryTextColor(style->GetPrimaryTextColor()); visualizerTemplate->SetSecondaryTextColor(style->GetSecondaryTextColor()); visualizerTemplate->SetItemSeparatorColor(style->GetItemSeparatorColor()); visualizerTemplate->SetLargeImage(listViewItemView->GetLargeImage(row)); visualizerTemplate->SetSmallImage(listViewItemView->GetSmallImage(row)); visualizerTemplate->SetText(column == 0 ? listViewItemView->GetText(row) : listViewItemView->GetSubItem(row, column - 1)); } if (auto dataGridView = dynamic_cast(dataGridContext->GetItemProvider()->RequestView(IDataGridView::Identifier))) { visualizerTemplate->SetRowValue(itemProvider->GetBindingValue(row)); visualizerTemplate->SetCellValue(dataGridView->GetBindingCellValue(row, column)); } } void DataVisualizerBase::SetSelected(bool value) { if (visualizerTemplate) { visualizerTemplate->SetSelected(value); } } /*********************************************************************** DataVisualizerFactory ***********************************************************************/ DataVisualizerFactory::ItemTemplate* DataVisualizerFactory::CreateItemTemplate(controls::list::IDataGridContext* dataGridContext) { ItemTemplate* itemTemplate = templateFactory({}); CHECK_ERROR(itemTemplate, L"DataVisualizerFactory::CreateItemTemplate(IDataGridContext*)#An instance of GuiGridEditorTemplate is expected."); if (decoratedFactory) { auto childTemplate = decoratedFactory->CreateItemTemplate(dataGridContext); childTemplate->SetAlignmentToParent(Margin(0, 0, 0, 0)); itemTemplate->GetContainerComposition()->AddChild(childTemplate); #define FORWARD_EVENT(NAME)\ itemTemplate->NAME##Changed.AttachLambda([=](GuiGraphicsComposition* sender, GuiEventArgs& arguments)\ {\ childTemplate->Set##NAME(itemTemplate->Get##NAME());\ });\ #define FORWARD_EVENT_IMPL(CLASS, TYPE, NAME, VALUE) FORWARD_EVENT(NAME) GuiTemplate_PROPERTIES(FORWARD_EVENT_IMPL) GuiControlTemplate_PROPERTIES(FORWARD_EVENT_IMPL) GuiGridCellTemplate_PROPERTIES(FORWARD_EVENT_IMPL) GuiGridVisualizerTemplate_PROPERTIES(FORWARD_EVENT_IMPL) #undef FORWARD_EVENT_IMPL #undef FORWARD_EVENT } return itemTemplate; } DataVisualizerFactory::DataVisualizerFactory(TemplateProperty _templateFactory, Ptr _decoratedFactory) :templateFactory(_templateFactory) , decoratedFactory(_decoratedFactory) { } DataVisualizerFactory::~DataVisualizerFactory() { } Ptr DataVisualizerFactory::CreateVisualizer(controls::list::IDataGridContext* dataGridContext) { auto dataVisualizer = MakePtr(); dataVisualizer->factory = this; dataVisualizer->dataGridContext = dataGridContext; dataVisualizer->visualizerTemplate = CreateItemTemplate(dataGridContext); return dataVisualizer; } /*********************************************************************** DataEditorBase ***********************************************************************/ void DataEditorBase::OnCellValueChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { dataGridContext->RequestSaveData(); } DataEditorBase::DataEditorBase() { } DataEditorBase::~DataEditorBase() { if (editorTemplate) { SafeDeleteComposition(editorTemplate); } } IDataEditorFactory* DataEditorBase::GetFactory() { return factory; } templates::GuiGridEditorTemplate* DataEditorBase::GetTemplate() { return editorTemplate; } void DataEditorBase::NotifyDeletedTemplate() { editorTemplate = nullptr; } void DataEditorBase::BeforeEditCell(GuiListControl::IItemProvider* itemProvider, vint row, vint column) { if (auto listViewItemView = dynamic_cast(dataGridContext->GetItemProvider()->RequestView(IListViewItemView::Identifier))) { auto style = dataGridContext->GetListViewControlTemplate(); editorTemplate->SetPrimaryTextColor(style->GetPrimaryTextColor()); editorTemplate->SetSecondaryTextColor(style->GetSecondaryTextColor()); editorTemplate->SetItemSeparatorColor(style->GetItemSeparatorColor()); editorTemplate->SetLargeImage(listViewItemView->GetLargeImage(row)); editorTemplate->SetSmallImage(listViewItemView->GetSmallImage(row)); editorTemplate->SetText(column == 0 ? listViewItemView->GetText(row) : listViewItemView->GetSubItem(row, column - 1)); } if (auto dataGridView = dynamic_cast(dataGridContext->GetItemProvider()->RequestView(IDataGridView::Identifier))) { editorTemplate->SetRowValue(itemProvider->GetBindingValue(row)); editorTemplate->SetCellValue(dataGridView->GetBindingCellValue(row, column)); } editorTemplate->CellValueChanged.AttachMethod(this, &DataEditorBase::OnCellValueChanged); } bool DataEditorBase::GetCellValueSaved() { if (editorTemplate) { return editorTemplate->GetCellValueSaved(); } return true; } /*********************************************************************** DataEditorFactory ***********************************************************************/ DataEditorFactory::DataEditorFactory(TemplateProperty _templateFactory) :templateFactory(_templateFactory) { } DataEditorFactory::~DataEditorFactory() { } Ptr DataEditorFactory::CreateEditor(controls::list::IDataGridContext* dataGridContext) { auto editor = MakePtr(); editor->factory = this; editor->dataGridContext = dataGridContext; ItemTemplate* itemTemplate = templateFactory({}); CHECK_ERROR(itemTemplate, L"DataEditorFactory::CreateEditor(IDataGridContext*)#An instance of GuiGridEditorTemplate is expected."); editor->editorTemplate = itemTemplate; return editor; } /*********************************************************************** MainColumnVisualizerTemplate ***********************************************************************/ void MainColumnVisualizerTemplate::OnTextChanged(GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { text->SetText(GetText()); } void MainColumnVisualizerTemplate::OnFontChanged(GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { text->SetFont(GetFont()); } void MainColumnVisualizerTemplate::OnTextColorChanged(GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { text->SetColor(GetPrimaryTextColor()); } void MainColumnVisualizerTemplate::OnSmallImageChanged(GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { auto imageData = GetSmallImage(); if (imageData) { image->SetImage(imageData->GetImage(), imageData->GetFrameIndex()); } else { image->SetImage(nullptr); } } MainColumnVisualizerTemplate::MainColumnVisualizerTemplate() { GuiTableComposition* table = new GuiTableComposition; table->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); table->SetRowsAndColumns(3, 2); table->SetRowOption(0, GuiCellOption::PercentageOption(0.5)); table->SetRowOption(1, GuiCellOption::MinSizeOption()); table->SetRowOption(2, GuiCellOption::PercentageOption(0.5)); table->SetColumnOption(0, GuiCellOption::MinSizeOption()); table->SetColumnOption(1, GuiCellOption::PercentageOption(1.0)); table->SetCellPadding(2); { GuiCellComposition* cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(1, 0, 1, 1); cell->SetPreferredMinSize(Size(16, 16)); image = GuiImageFrameElement::Create(); image->SetStretch(true); cell->SetOwnedElement(image); } { GuiCellComposition* cell = new GuiCellComposition; table->AddChild(cell); cell->SetSite(0, 1, 3, 1); cell->SetMargin(Margin(0, 0, 8, 0)); text = GuiSolidLabelElement::Create(); text->SetAlignments(Alignment::Left, Alignment::Center); text->SetEllipse(true); cell->SetOwnedElement(text); } table->SetAlignmentToParent(Margin(0, 0, 0, 0)); SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); AddChild(table); TextChanged.AttachMethod(this, &MainColumnVisualizerTemplate::OnTextChanged); FontChanged.AttachMethod(this, &MainColumnVisualizerTemplate::OnFontChanged); PrimaryTextColorChanged.AttachMethod(this, &MainColumnVisualizerTemplate::OnTextColorChanged); SmallImageChanged.AttachMethod(this, &MainColumnVisualizerTemplate::OnSmallImageChanged); TextChanged.Execute(compositions::GuiEventArgs(this)); FontChanged.Execute(compositions::GuiEventArgs(this)); PrimaryTextColorChanged.Execute(compositions::GuiEventArgs(this)); SmallImageChanged.Execute(compositions::GuiEventArgs(this)); } MainColumnVisualizerTemplate::~MainColumnVisualizerTemplate() { } /*********************************************************************** SubColumnVisualizerTemplate ***********************************************************************/ void SubColumnVisualizerTemplate::OnTextChanged(GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { text->SetText(GetText()); } void SubColumnVisualizerTemplate::OnFontChanged(GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { text->SetFont(GetFont()); } void SubColumnVisualizerTemplate::OnTextColorChanged(GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { text->SetColor(GetSecondaryTextColor()); } void SubColumnVisualizerTemplate::Initialize(bool fixTextColor) { text = GuiSolidLabelElement::Create(); text->SetVerticalAlignment(Alignment::Center); SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); SetMargin(Margin(8, 0, 8, 0)); SetOwnedElement(text); TextChanged.AttachMethod(this, &SubColumnVisualizerTemplate::OnTextChanged); FontChanged.AttachMethod(this, &SubColumnVisualizerTemplate::OnFontChanged); if (!fixTextColor) { SecondaryTextColorChanged.AttachMethod(this, &SubColumnVisualizerTemplate::OnTextColorChanged); } TextChanged.Execute(compositions::GuiEventArgs(this)); FontChanged.Execute(compositions::GuiEventArgs(this)); if (!fixTextColor) { SecondaryTextColorChanged.Execute(compositions::GuiEventArgs(this)); } } SubColumnVisualizerTemplate::SubColumnVisualizerTemplate(bool fixTextColor) { Initialize(fixTextColor); } SubColumnVisualizerTemplate::SubColumnVisualizerTemplate() { Initialize(false); } SubColumnVisualizerTemplate::~SubColumnVisualizerTemplate() { } /*********************************************************************** HyperlinkVisualizerTemplate ***********************************************************************/ void HyperlinkVisualizerTemplate::label_MouseEnter(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { FontProperties font = text->GetFont(); font.underline = true; text->SetFont(font); } void HyperlinkVisualizerTemplate::label_MouseLeave(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { FontProperties font = text->GetFont(); font.underline = false; text->SetFont(font); } HyperlinkVisualizerTemplate::HyperlinkVisualizerTemplate() :SubColumnVisualizerTemplate(true) { text->SetColor(Color(0, 0, 255)); text->SetEllipse(true); GetEventReceiver()->mouseEnter.AttachMethod(this, &HyperlinkVisualizerTemplate::label_MouseEnter); GetEventReceiver()->mouseLeave.AttachMethod(this, &HyperlinkVisualizerTemplate::label_MouseLeave); SetAssociatedCursor(GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::Hand)); } HyperlinkVisualizerTemplate::~HyperlinkVisualizerTemplate() { } /*********************************************************************** CellBorderVisualizerTemplate ***********************************************************************/ void CellBorderVisualizerTemplate::OnItemSeparatorColorChanged(GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { border1->SetColor(GetItemSeparatorColor()); border2->SetColor(GetItemSeparatorColor()); } CellBorderVisualizerTemplate::CellBorderVisualizerTemplate() { GuiBoundsComposition* bounds1 = nullptr; GuiBoundsComposition* bounds2 = nullptr; { border1 = GuiSolidBorderElement::Create(); bounds1 = new GuiBoundsComposition; bounds1->SetOwnedElement(border1); bounds1->SetAlignmentToParent(Margin(-1, 0, 0, 0)); } { border2 = GuiSolidBorderElement::Create(); bounds2 = new GuiBoundsComposition; bounds2->SetOwnedElement(border2); bounds2->SetAlignmentToParent(Margin(0, -1, 0, 0)); } SetAlignmentToParent(Margin(0, 0, 1, 1)); SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); AddChild(bounds1); AddChild(bounds2); ItemSeparatorColorChanged.AttachMethod(this, &CellBorderVisualizerTemplate::OnItemSeparatorColorChanged); ItemSeparatorColorChanged.Execute(compositions::GuiEventArgs(this)); } CellBorderVisualizerTemplate::~CellBorderVisualizerTemplate() { } } } } } /*********************************************************************** .\CONTROLS\TEXTEDITORPACKAGE\EDITORCALLBACK\GUITEXTAUTOCOMPLETE.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace collections; /*********************************************************************** GuiTextBoxAutoCompleteBase::TextListControlProvider ***********************************************************************/ GuiTextBoxAutoCompleteBase::TextListControlProvider::TextListControlProvider(TemplateProperty controlTemplate) { autoCompleteList = new GuiTextList(theme::ThemeName::TextList); if (controlTemplate) { autoCompleteList->SetControlTemplate(controlTemplate); } autoCompleteList->SetHorizontalAlwaysVisible(false); autoCompleteList->SetVerticalAlwaysVisible(false); } GuiTextBoxAutoCompleteBase::TextListControlProvider::~TextListControlProvider() { } GuiControl* GuiTextBoxAutoCompleteBase::TextListControlProvider::GetAutoCompleteControl() { return autoCompleteList; } GuiSelectableListControl* GuiTextBoxAutoCompleteBase::TextListControlProvider::GetListControl() { return autoCompleteList; } void GuiTextBoxAutoCompleteBase::TextListControlProvider::SetSortedContent(const collections::List& items) { autoCompleteList->GetItems().Clear(); FOREACH(AutoCompleteItem, item, items) { autoCompleteList->GetItems().Add(new list::TextItem(item.text)); } } vint GuiTextBoxAutoCompleteBase::TextListControlProvider::GetItemCount() { return autoCompleteList->GetItems().Count(); } WString GuiTextBoxAutoCompleteBase::TextListControlProvider::GetItemText(vint index) { return autoCompleteList->GetItems()[index]->GetText(); } /*********************************************************************** GuiTextBoxAutoCompleteBase ***********************************************************************/ bool GuiTextBoxAutoCompleteBase::IsPrefix(const WString& prefix, const WString& candidate) { if(candidate.Length()>=prefix.Length()) { if(INVLOC.Compare(prefix, candidate.Sub(0, prefix.Length()), Locale::IgnoreCase)==0) { return true; } } return false; } GuiTextBoxAutoCompleteBase::GuiTextBoxAutoCompleteBase(Ptr _autoCompleteControlProvider) :element(0) , elementModifyLock(0) , ownerComposition(0) , autoCompleteControlProvider(_autoCompleteControlProvider) { if (!autoCompleteControlProvider) { autoCompleteControlProvider = new TextListControlProvider; } autoCompleteControlProvider->GetAutoCompleteControl()->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); autoCompletePopup = new GuiPopup(theme::ThemeName::Menu); autoCompletePopup->AddChild(autoCompleteControlProvider->GetAutoCompleteControl()); } GuiTextBoxAutoCompleteBase::~GuiTextBoxAutoCompleteBase() { delete autoCompletePopup; } void GuiTextBoxAutoCompleteBase::Attach(elements::GuiColorizedTextElement* _element, SpinLock& _elementModifyLock, compositions::GuiGraphicsComposition* _ownerComposition, vuint editVersion) { if(_element) { SPIN_LOCK(_elementModifyLock) { element=_element; elementModifyLock=&_elementModifyLock; ownerComposition=_ownerComposition; } } } void GuiTextBoxAutoCompleteBase::Detach() { if(element && elementModifyLock) { SPIN_LOCK(*elementModifyLock) { element=0; elementModifyLock=0; } } } void GuiTextBoxAutoCompleteBase::TextEditPreview(TextEditPreviewStruct& arguments) { } void GuiTextBoxAutoCompleteBase::TextEditNotify(const TextEditNotifyStruct& arguments) { if(element && elementModifyLock) { if(IsListOpening()) { TextPos begin=GetListStartPosition(); TextPos end=arguments.inputEnd; WString editingText=element->GetLines().GetText(begin, end); HighlightList(editingText); } } } void GuiTextBoxAutoCompleteBase::TextCaretChanged(const TextCaretChangedStruct& arguments) { } void GuiTextBoxAutoCompleteBase::TextEditFinished(vuint editVersion) { } bool GuiTextBoxAutoCompleteBase::IsListOpening() { return autoCompletePopup->GetOpening(); } void GuiTextBoxAutoCompleteBase::OpenList(TextPos startPosition) { if(element && elementModifyLock) { autoCompleteStartPosition=startPosition; Rect bounds=element->GetLines().GetRectFromTextPos(startPosition); Point viewPosition=element->GetViewPosition(); GuiControl* ownerControl=ownerComposition->GetRelatedControl(); Rect compositionBounds=ownerComposition->GetGlobalBounds(); Rect controlBounds=ownerControl->GetBoundsComposition()->GetGlobalBounds(); vint px=compositionBounds.x1-controlBounds.x1-viewPosition.x; vint py=compositionBounds.y1-controlBounds.y1-viewPosition.y; bounds.x1+=px; bounds.x2+=px; bounds.y1+=py+5; bounds.y2+=py+5; autoCompletePopup->ShowPopup(ownerControl, bounds, true); } } void GuiTextBoxAutoCompleteBase::CloseList() { autoCompletePopup->Close(); } void GuiTextBoxAutoCompleteBase::SetListContent(const collections::List& items) { if(items.Count()==0) { CloseList(); } List sortedItems; CopyFrom( sortedItems, From(items) .OrderBy([](const AutoCompleteItem& a, const AutoCompleteItem& b) { return INVLOC.Compare(a.text, b.text, Locale::IgnoreCase); }) ); autoCompleteControlProvider->SetSortedContent(sortedItems); autoCompleteControlProvider->GetAutoCompleteControl()->GetBoundsComposition()->SetPreferredMinSize(Size(200, 200)); } TextPos GuiTextBoxAutoCompleteBase::GetListStartPosition() { return autoCompleteStartPosition; } bool GuiTextBoxAutoCompleteBase::SelectPreviousListItem() { if(!IsListOpening()) return false; if(autoCompleteControlProvider->GetListControl()->GetSelectedItems().Count()==0) { autoCompleteControlProvider->GetListControl()->SetSelected(0, true); } else { vint index=autoCompleteControlProvider->GetListControl()->GetSelectedItems()[0]; if (index > 0) index--; autoCompleteControlProvider->GetListControl()->SetSelected(index, true); autoCompleteControlProvider->GetListControl()->EnsureItemVisible(index); } return true; } bool GuiTextBoxAutoCompleteBase::SelectNextListItem() { if(!IsListOpening()) return false; if (autoCompleteControlProvider->GetListControl()->GetSelectedItems().Count() == 0) { autoCompleteControlProvider->GetListControl()->SetSelected(0, true); } else { vint index = autoCompleteControlProvider->GetListControl()->GetSelectedItems()[0]; if (index < autoCompleteControlProvider->GetItemCount() - 1) index++; autoCompleteControlProvider->GetListControl()->SetSelected(index, true); autoCompleteControlProvider->GetListControl()->EnsureItemVisible(index); } return true; } bool GuiTextBoxAutoCompleteBase::ApplySelectedListItem() { if(!IsListOpening()) return false; if(!ownerComposition) return false; const auto& selectedItems = autoCompleteControlProvider->GetListControl()->GetSelectedItems(); if (selectedItems.Count() == 0) return false; GuiTextBoxCommonInterface* ci=dynamic_cast(ownerComposition->GetRelatedControl()); if(!ci) return false; vint index = selectedItems[0]; WString selectedItem = autoCompleteControlProvider->GetItemText(index); TextPos begin = autoCompleteStartPosition; TextPos end = ci->GetCaretEnd(); ci->Select(begin, end); ci->SetSelectionText(selectedItem); CloseList(); return true; } WString GuiTextBoxAutoCompleteBase::GetSelectedListItem() { if(!IsListOpening()) return L""; const auto& selectedItems = autoCompleteControlProvider->GetListControl()->GetSelectedItems(); if (selectedItems.Count() == 0) return L""; vint index = selectedItems[0]; return autoCompleteControlProvider->GetItemText(index); } void GuiTextBoxAutoCompleteBase::HighlightList(const WString& editingText) { if(IsListOpening()) { vint first=0; vint last = autoCompleteControlProvider->GetItemCount() - 1; vint selected=-1; while (first <= last) { vint middle = (first + last) / 2; WString text = autoCompleteControlProvider->GetItemText(middle); if (IsPrefix(editingText, text)) { selected = middle; break; } vint result = INVLOC.Compare(editingText, text, Locale::IgnoreCase); if (result <= 0) { last = middle - 1; } else { first = middle + 1; } } while(selected>0) { WString text = autoCompleteControlProvider->GetItemText(selected - 1); if (IsPrefix(editingText, text)) { selected--; } else { break; } } if(selected!=-1) { autoCompleteControlProvider->GetListControl()->SetSelected(selected, true); autoCompleteControlProvider->GetListControl()->EnsureItemVisible(selected); } } } } } } /*********************************************************************** .\CONTROLS\TEXTEDITORPACKAGE\GUITEXTCOMMONINTERFACE.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace elements; using namespace elements::text; using namespace compositions; /*********************************************************************** GuiTextBoxCommonInterface::DefaultCallback ***********************************************************************/ GuiTextBoxCommonInterface::DefaultCallback::DefaultCallback(elements::GuiColorizedTextElement* _textElement, compositions::GuiGraphicsComposition* _textComposition) :textElement(_textElement) ,textComposition(_textComposition) { } GuiTextBoxCommonInterface::DefaultCallback::~DefaultCallback() { } TextPos GuiTextBoxCommonInterface::DefaultCallback::GetLeftWord(TextPos pos) { return pos; } TextPos GuiTextBoxCommonInterface::DefaultCallback::GetRightWord(TextPos pos) { return pos; } void GuiTextBoxCommonInterface::DefaultCallback::GetWord(TextPos pos, TextPos& begin, TextPos& end) { begin=pos; end=pos; } vint GuiTextBoxCommonInterface::DefaultCallback::GetPageRows() { return textComposition->GetBounds().Height()/textElement->GetLines().GetRowHeight(); } bool GuiTextBoxCommonInterface::DefaultCallback::BeforeModify(TextPos start, TextPos end, const WString& originalText, WString& inputText) { return true; } /*********************************************************************** GuiTextBoxCommonInterface ***********************************************************************/ void GuiTextBoxCommonInterface::InvokeUndoRedoChanged() { UndoRedoChanged.Execute(textControl->GetNotifyEventArguments()); } void GuiTextBoxCommonInterface::InvokeModifiedChanged() { ModifiedChanged.Execute(textControl->GetNotifyEventArguments()); } void GuiTextBoxCommonInterface::UpdateCaretPoint() { GuiGraphicsHost* host=textComposition->GetRelatedGraphicsHost(); if(host) { Rect caret=textElement->GetLines().GetRectFromTextPos(textElement->GetCaretEnd()); Point view=textElement->GetViewPosition(); vint x=caret.x1-view.x; vint y=caret.y2-view.y; host->SetCaretPoint(Point(x, y), textComposition); } } void GuiTextBoxCommonInterface::Move(TextPos pos, bool shift) { TextPos oldBegin=textElement->GetCaretBegin(); TextPos oldEnd=textElement->GetCaretEnd(); pos=textElement->GetLines().Normalize(pos); if(!shift) { textElement->SetCaretBegin(pos); } textElement->SetCaretEnd(pos); if(textControl) { GuiGraphicsHost* host=textComposition->GetRelatedGraphicsHost(); if(host) { if(host->GetFocusedComposition()==textControl->GetFocusableComposition()) { textElement->SetCaretVisible(true); } } } Rect bounds=textElement->GetLines().GetRectFromTextPos(pos); Rect view=Rect(textElement->GetViewPosition(), textComposition->GetBounds().GetSize()); Point viewPoint=view.LeftTop(); if(view.x2>view.x1 && view.y2>view.y1) { if(bounds.x1view.x2) { viewPoint.x=bounds.x2-view.Width(); } if(bounds.y1view.y2) { viewPoint.y=bounds.y2-view.Height(); } } callback->ScrollToView(viewPoint); UpdateCaretPoint(); TextPos newBegin=textElement->GetCaretBegin(); TextPos newEnd=textElement->GetCaretEnd(); if(oldBegin!=newBegin || oldEnd!=newEnd) { ICommonTextEditCallback::TextCaretChangedStruct arguments; arguments.oldBegin=oldBegin; arguments.oldEnd=oldEnd; arguments.newBegin=newBegin; arguments.newEnd=newEnd; arguments.editVersion=editVersion; for(vint i=0;iTextCaretChanged(arguments); } SelectionChanged.Execute(textControl->GetNotifyEventArguments()); } } void GuiTextBoxCommonInterface::Modify(TextPos start, TextPos end, const WString& input, bool asKeyInput) { if(start>end) { TextPos temp=start; start=end; end=temp; } TextPos originalStart=start; TextPos originalEnd=end; WString originalText=textElement->GetLines().GetText(start, end); WString inputText=input; if(callback->BeforeModify(start, end, originalText, inputText)) { { ICommonTextEditCallback::TextEditPreviewStruct arguments; arguments.originalStart=originalStart; arguments.originalEnd=originalEnd; arguments.originalText=originalText; arguments.inputText=inputText; arguments.editVersion=editVersion; arguments.keyInput=asKeyInput; for(vint i=0;iTextEditPreview(arguments); } inputText=arguments.inputText; if(originalStart!=arguments.originalStart || originalEnd!=arguments.originalEnd) { originalStart=arguments.originalStart; originalEnd=arguments.originalEnd; originalText=textElement->GetLines().GetText(originalStart, originalEnd); start=originalStart; end=originalEnd; } } SPIN_LOCK(elementModifyLock) { end=textElement->GetLines().Modify(start, end, inputText); } callback->AfterModify(originalStart, originalEnd, originalText, start, end, inputText); editVersion++; { ICommonTextEditCallback::TextEditNotifyStruct arguments; arguments.originalStart=originalStart; arguments.originalEnd=originalEnd; arguments.originalText=originalText; arguments.inputStart=start; arguments.inputEnd=end; arguments.inputText=inputText; arguments.editVersion=editVersion; arguments.keyInput=asKeyInput; for(vint i=0;iTextEditNotify(arguments); } } Move(end, false); for(vint i=0;iTextEditFinished(editVersion); } textControl->TextChanged.Execute(textControl->GetNotifyEventArguments()); } } bool GuiTextBoxCommonInterface::ProcessKey(vint code, bool shift, bool ctrl) { if(IGuiShortcutKeyItem* item=internalShortcutKeyManager->TryGetShortcut(ctrl, shift, false, code)) { GuiEventArgs arguments; item->Executed.Execute(arguments); return true; } TextPos begin=textElement->GetCaretBegin(); TextPos end=textElement->GetCaretEnd(); switch(code) { case VKEY_ESCAPE: if(autoComplete && autoComplete->IsListOpening() && !shift && !ctrl) { autoComplete->CloseList(); } return true; case VKEY_RETURN: if(autoComplete && autoComplete->IsListOpening() && !shift && !ctrl) { if(autoComplete->ApplySelectedListItem()) { preventEnterDueToAutoComplete=true; return true; } } break; case VKEY_UP: if(autoComplete && autoComplete->IsListOpening() && !shift && !ctrl) { autoComplete->SelectPreviousListItem(); } else { end.row--; Move(end, shift); } return true; case VKEY_DOWN: if(autoComplete && autoComplete->IsListOpening() && !shift && !ctrl) { autoComplete->SelectNextListItem(); } else { end.row++; Move(end, shift); } return true; case VKEY_LEFT: { if(ctrl) { Move(callback->GetLeftWord(end), shift); } else { if(end.column==0) { if(end.row>0) { end.row--; end=textElement->GetLines().Normalize(end); end.column=textElement->GetLines().GetLine(end.row).dataLength; } } else { end.column--; } Move(end, shift); } } return true; case VKEY_RIGHT: { if(ctrl) { Move(callback->GetRightWord(end), shift); } else { if(end.column==textElement->GetLines().GetLine(end.row).dataLength) { if(end.rowGetLines().GetCount()-1) { end.row++; end.column=0; } } else { end.column++; } Move(end, shift); } } return true; case VKEY_HOME: { if(ctrl) { Move(TextPos(0, 0), shift); } else { end.column=0; Move(end, shift); } } return true; case VKEY_END: { if(ctrl) { end.row=textElement->GetLines().GetCount()-1; } end.column=textElement->GetLines().GetLine(end.row).dataLength; Move(end, shift); } return true; case VKEY_PRIOR: { end.row-=callback->GetPageRows(); Move(end, shift); } return true; case VKEY_NEXT: { end.row+=callback->GetPageRows(); Move(end, shift); } return true; case VKEY_BACK: if(!readonly) { if(ctrl && !shift) { ProcessKey(VKEY_LEFT, true, true); ProcessKey(VKEY_BACK, false, false); } else if(!ctrl && shift) { ProcessKey(VKEY_UP, true, false); ProcessKey(VKEY_BACK, false, false); } else { if(begin==end) { ProcessKey(VKEY_LEFT, true, false); } SetSelectionTextAsKeyInput(L""); } return true; } break; case VKEY_DELETE: if(!readonly) { if(ctrl && !shift) { ProcessKey(VKEY_RIGHT, true, true); ProcessKey(VKEY_DELETE, false, false); } else if(!ctrl && shift) { ProcessKey(VKEY_DOWN, true, false); ProcessKey(VKEY_DELETE, false, false); } else { if(begin==end) { ProcessKey(VKEY_RIGHT, true, false); } SetSelectionTextAsKeyInput(L""); } return true; } break; } return false; } void GuiTextBoxCommonInterface::OnGotFocus(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { textElement->SetFocused(true); textElement->SetCaretVisible(true); UpdateCaretPoint(); } void GuiTextBoxCommonInterface::OnLostFocus(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { textElement->SetFocused(false); textElement->SetCaretVisible(false); } void GuiTextBoxCommonInterface::OnCaretNotify(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { textElement->SetCaretVisible(!textElement->GetCaretVisible()); } void GuiTextBoxCommonInterface::OnLeftButtonDown(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { if(textControl->GetVisuallyEnabled() && arguments.compositionSource==arguments.eventSource) { dragging=true; TextPos pos=GetNearestTextPos(Point(arguments.x, arguments.y)); Move(pos, arguments.shift); } } void GuiTextBoxCommonInterface::OnLeftButtonUp(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { if(textControl->GetVisuallyEnabled() && arguments.compositionSource==arguments.eventSource) { dragging=false; } } void GuiTextBoxCommonInterface::OnMouseMove(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { if(textControl->GetVisuallyEnabled() && arguments.compositionSource==arguments.eventSource) { if(dragging) { TextPos pos=GetNearestTextPos(Point(arguments.x, arguments.y)); Move(pos, true); } } } void GuiTextBoxCommonInterface::OnKeyDown(compositions::GuiGraphicsComposition* sender, compositions::GuiKeyEventArgs& arguments) { if(textControl->GetVisuallyEnabled() && arguments.compositionSource==arguments.eventSource) { if(ProcessKey(arguments.code, arguments.shift, arguments.ctrl)) { arguments.handled=true; } } } void GuiTextBoxCommonInterface::OnCharInput(compositions::GuiGraphicsComposition* sender, compositions::GuiCharEventArgs& arguments) { if(preventEnterDueToAutoComplete) { preventEnterDueToAutoComplete=false; if(arguments.code==VKEY_RETURN) { return; } } if(textControl->GetVisuallyEnabled() && arguments.compositionSource==arguments.eventSource) { if(!readonly && arguments.code!=VKEY_ESCAPE && arguments.code!=VKEY_BACK && !arguments.ctrl) { SetSelectionTextAsKeyInput(WString(arguments.code)); } } } void GuiTextBoxCommonInterface::Install( elements::GuiColorizedTextElement* _textElement, compositions::GuiGraphicsComposition* _textComposition, GuiControl* _textControl, compositions::GuiGraphicsComposition* eventComposition, compositions::GuiGraphicsComposition* focusableComposition ) { textElement=_textElement; textComposition=_textComposition; textControl=_textControl; textComposition->SetAssociatedCursor(GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::IBeam)); SelectionChanged.SetAssociatedComposition(eventComposition); UndoRedoChanged.SetAssociatedComposition(eventComposition); ModifiedChanged.SetAssociatedComposition(eventComposition); undoRedoProcessor->UndoRedoChanged.Add(this, &GuiTextBoxCommonInterface::InvokeUndoRedoChanged); undoRedoProcessor->ModifiedChanged.Add(this, &GuiTextBoxCommonInterface::InvokeModifiedChanged); focusableComposition->GetEventReceiver()->gotFocus.AttachMethod(this, &GuiTextBoxCommonInterface::OnGotFocus); focusableComposition->GetEventReceiver()->lostFocus.AttachMethod(this, &GuiTextBoxCommonInterface::OnLostFocus); focusableComposition->GetEventReceiver()->caretNotify.AttachMethod(this, &GuiTextBoxCommonInterface::OnCaretNotify); textComposition->GetEventReceiver()->leftButtonDown.AttachMethod(this, &GuiTextBoxCommonInterface::OnLeftButtonDown); textComposition->GetEventReceiver()->leftButtonUp.AttachMethod(this, &GuiTextBoxCommonInterface::OnLeftButtonUp); textComposition->GetEventReceiver()->mouseMove.AttachMethod(this, &GuiTextBoxCommonInterface::OnMouseMove); focusableComposition->GetEventReceiver()->keyDown.AttachMethod(this, &GuiTextBoxCommonInterface::OnKeyDown); focusableComposition->GetEventReceiver()->charInput.AttachMethod(this, &GuiTextBoxCommonInterface::OnCharInput); for(vint i=0;iAttach(textElement, elementModifyLock, textComposition ,editVersion); } } GuiTextBoxCommonInterface::ICallback* GuiTextBoxCommonInterface::GetCallback() { return callback; } void GuiTextBoxCommonInterface::SetCallback(ICallback* value) { callback=value; } bool GuiTextBoxCommonInterface::AttachTextEditCallback(Ptr value) { if(textEditCallbacks.Contains(value.Obj())) { return false; } else { textEditCallbacks.Add(value); if(textElement) { value->Attach(textElement, elementModifyLock, textComposition, editVersion); } return true; } } bool GuiTextBoxCommonInterface::DetachTextEditCallback(Ptr value) { if(textEditCallbacks.Remove(value.Obj())) { value->Detach(); return true; } else { return false; } } void GuiTextBoxCommonInterface::AddShortcutCommand(vint key, const Func& eventHandler) { IGuiShortcutKeyItem* item=internalShortcutKeyManager->CreateShortcut(true, false, false, key); item->Executed.AttachLambda([=](GuiGraphicsComposition* sender, GuiEventArgs& arguments) { eventHandler(); }); } elements::GuiColorizedTextElement* GuiTextBoxCommonInterface::GetTextElement() { return textElement; } void GuiTextBoxCommonInterface::UnsafeSetText(const WString& value) { if(textElement) { TextPos end; if(textElement->GetLines().GetCount()>0) { end.row=textElement->GetLines().GetCount()-1; end.column=textElement->GetLines().GetLine(end.row).dataLength; } Modify(TextPos(), end, value, false); } } GuiTextBoxCommonInterface::GuiTextBoxCommonInterface() :textElement(0) ,textComposition(0) ,editVersion(0) ,textControl(0) ,callback(0) ,dragging(false) ,readonly(false) ,preventEnterDueToAutoComplete(false) { undoRedoProcessor=new GuiTextBoxUndoRedoProcessor; AttachTextEditCallback(undoRedoProcessor); internalShortcutKeyManager=new GuiShortcutKeyManager; AddShortcutCommand('Z', Func(this, &GuiTextBoxCommonInterface::Undo)); AddShortcutCommand('Y', Func(this, &GuiTextBoxCommonInterface::Redo)); AddShortcutCommand('A', Func(this, &GuiTextBoxCommonInterface::SelectAll)); AddShortcutCommand('X', Func(this, &GuiTextBoxCommonInterface::Cut)); AddShortcutCommand('C', Func(this, &GuiTextBoxCommonInterface::Copy)); AddShortcutCommand('V', Func(this, &GuiTextBoxCommonInterface::Paste)); } GuiTextBoxCommonInterface::~GuiTextBoxCommonInterface() { if(colorizer) { DetachTextEditCallback(colorizer); colorizer=0; } if(undoRedoProcessor) { DetachTextEditCallback(undoRedoProcessor); undoRedoProcessor=0; } for(vint i=0;iDetach(); } textEditCallbacks.Clear(); } //================ clipboard operations bool GuiTextBoxCommonInterface::CanCut() { return !readonly && textElement->GetCaretBegin()!=textElement->GetCaretEnd() && textElement->GetPasswordChar()==L'\0'; } bool GuiTextBoxCommonInterface::CanCopy() { return textElement->GetCaretBegin()!=textElement->GetCaretEnd() && textElement->GetPasswordChar()==L'\0'; } bool GuiTextBoxCommonInterface::CanPaste() { return !readonly && GetCurrentController()->ClipboardService()->ContainsText() && textElement->GetPasswordChar()==L'\0'; } bool GuiTextBoxCommonInterface::Cut() { if(CanCut()) { GetCurrentController()->ClipboardService()->SetText(GetSelectionText()); SetSelectionText(L""); return true; } else { return false; } } bool GuiTextBoxCommonInterface::Copy() { if(CanCopy()) { GetCurrentController()->ClipboardService()->SetText(GetSelectionText()); return true; } else { return false; } } bool GuiTextBoxCommonInterface::Paste() { if(CanPaste()) { SetSelectionText(GetCurrentController()->ClipboardService()->GetText()); return true; } else { return false; } } //================ editing control bool GuiTextBoxCommonInterface::GetReadonly() { return readonly; } void GuiTextBoxCommonInterface::SetReadonly(bool value) { readonly=value; } //================ text operations void GuiTextBoxCommonInterface::Select(TextPos begin, TextPos end) { Move(begin, false); Move(end, true); } void GuiTextBoxCommonInterface::SelectAll() { vint row=textElement->GetLines().GetCount()-1; Move(TextPos(0, 0), false); Move(TextPos(row, textElement->GetLines().GetLine(row).dataLength), true); } WString GuiTextBoxCommonInterface::GetSelectionText() { TextPos selectionBegin=textElement->GetCaretBegin()GetCaretEnd()?textElement->GetCaretBegin():textElement->GetCaretEnd(); TextPos selectionEnd=textElement->GetCaretBegin()>textElement->GetCaretEnd()?textElement->GetCaretBegin():textElement->GetCaretEnd(); return textElement->GetLines().GetText(selectionBegin, selectionEnd); } void GuiTextBoxCommonInterface::SetSelectionText(const WString& value) { Modify(textElement->GetCaretBegin(), textElement->GetCaretEnd(), value, false); } void GuiTextBoxCommonInterface::SetSelectionTextAsKeyInput(const WString& value) { Modify(textElement->GetCaretBegin(), textElement->GetCaretEnd(), value, true); } WString GuiTextBoxCommonInterface::GetRowText(vint row) { TextPos start=textElement->GetLines().Normalize(TextPos(row, 0)); TextPos end=TextPos(start.row, textElement->GetLines().GetLine(start.row).dataLength); return GetFragmentText(start, end); } vint GuiTextBoxCommonInterface::GetRowCount() { return textElement->GetLines().GetCount(); } WString GuiTextBoxCommonInterface::GetFragmentText(TextPos start, TextPos end) { start=textElement->GetLines().Normalize(start); end=textElement->GetLines().Normalize(end); return textElement->GetLines().GetText(start, end); } TextPos GuiTextBoxCommonInterface::GetCaretBegin() { return textElement->GetCaretBegin(); } TextPos GuiTextBoxCommonInterface::GetCaretEnd() { return textElement->GetCaretEnd(); } TextPos GuiTextBoxCommonInterface::GetCaretSmall() { TextPos c1=GetCaretBegin(); TextPos c2=GetCaretEnd(); return c1c2?c1:c2; } //================ position query vint GuiTextBoxCommonInterface::GetRowWidth(vint row) { return textElement->GetLines().GetRowWidth(row); } vint GuiTextBoxCommonInterface::GetRowHeight() { return textElement->GetLines().GetRowHeight(); } vint GuiTextBoxCommonInterface::GetMaxWidth() { return textElement->GetLines().GetMaxWidth(); } vint GuiTextBoxCommonInterface::GetMaxHeight() { return textElement->GetLines().GetMaxHeight(); } TextPos GuiTextBoxCommonInterface::GetTextPosFromPoint(Point point) { Point view=textElement->GetViewPosition(); return textElement->GetLines().GetTextPosFromPoint(Point(point.x+view.x, point.y+view.y)); } Point GuiTextBoxCommonInterface::GetPointFromTextPos(TextPos pos) { Point view=textElement->GetViewPosition(); Point result=textElement->GetLines().GetPointFromTextPos(pos); return Point(result.x-view.x, result.y-view.y); } Rect GuiTextBoxCommonInterface::GetRectFromTextPos(TextPos pos) { Point view=textElement->GetViewPosition(); Rect result=textElement->GetLines().GetRectFromTextPos(pos); return Rect(Point(result.x1-view.x, result.y1-view.y), result.GetSize()); } TextPos GuiTextBoxCommonInterface::GetNearestTextPos(Point point) { Point viewPosition=textElement->GetViewPosition(); Point mousePosition=Point(point.x+viewPosition.x, point.y+viewPosition.y); TextPos pos=textElement->GetLines().GetTextPosFromPoint(mousePosition); if(pos.columnGetLines().GetLine(pos.row).dataLength) { Rect rect=textElement->GetLines().GetRectFromTextPos(pos); if(abs((int)(rect.x1-mousePosition.x))>=abs((int)(rect.x2-1-mousePosition.x))) { pos.column++; } } return pos; } //================ colorizing Ptr GuiTextBoxCommonInterface::GetColorizer() { return colorizer; } void GuiTextBoxCommonInterface::SetColorizer(Ptr value) { if (!filledDefaultColors) { filledDefaultColors = true; CopyFrom(defaultColors, GetTextElement()->GetColors()); } if(colorizer) { DetachTextEditCallback(colorizer); } colorizer=value; if(colorizer) { AttachTextEditCallback(colorizer); GetTextElement()->SetColors(colorizer->GetColors()); } else { GetTextElement()->SetColors(defaultColors); GetTextElement()->ResetTextColorIndex(0); } } //================ auto complete Ptr GuiTextBoxCommonInterface::GetAutoComplete() { return autoComplete; } void GuiTextBoxCommonInterface::SetAutoComplete(Ptr value) { if(autoComplete) { DetachTextEditCallback(autoComplete); } autoComplete=value; if(autoComplete) { AttachTextEditCallback(autoComplete); } } //================ undo redo control vuint GuiTextBoxCommonInterface::GetEditVersion() { return editVersion; } bool GuiTextBoxCommonInterface::CanUndo() { return !readonly && undoRedoProcessor->CanUndo(); } bool GuiTextBoxCommonInterface::CanRedo() { return !readonly && undoRedoProcessor->CanRedo(); } void GuiTextBoxCommonInterface::ClearUndoRedo() { undoRedoProcessor->ClearUndoRedo(); } bool GuiTextBoxCommonInterface::GetModified() { return undoRedoProcessor->GetModified(); } void GuiTextBoxCommonInterface::NotifyModificationSaved() { undoRedoProcessor->NotifyModificationSaved(); } bool GuiTextBoxCommonInterface::Undo() { if(CanUndo()) { return undoRedoProcessor->Undo(); } else { return false; } } bool GuiTextBoxCommonInterface::Redo() { if(CanRedo()) { return undoRedoProcessor->Redo(); } else { return false; } } } } } /*********************************************************************** .\CONTROLS\TEXTEDITORPACKAGE\GUITEXTCONTROLS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace elements; using namespace elements::text; using namespace compositions; using namespace collections; /*********************************************************************** GuiMultilineTextBox::DefaultTextElementOperatorCallback ***********************************************************************/ GuiMultilineTextBox::TextElementOperatorCallback::TextElementOperatorCallback(GuiMultilineTextBox* _textControl) :GuiTextBoxCommonInterface::DefaultCallback( _textControl->textElement, _textControl->textComposition ) ,textControl(_textControl) { } void GuiMultilineTextBox::TextElementOperatorCallback::AfterModify(TextPos originalStart, TextPos originalEnd, const WString& originalText, TextPos inputStart, TextPos inputEnd, const WString& inputText) { textControl->CalculateView(); } void GuiMultilineTextBox::TextElementOperatorCallback::ScrollToView(Point point) { point.x+=TextMargin; point.y+=TextMargin; Point oldPoint = textControl->GetViewPosition(); vint marginX=0; vint marginY=0; if(oldPoint.xpoint.x) { marginX=-TextMargin; } if(oldPoint.ypoint.y) { marginY=-TextMargin; } textControl->SetViewPosition(Point(point.x + marginX, point.y + marginY)); } vint GuiMultilineTextBox::TextElementOperatorCallback::GetTextMargin() { return TextMargin; } /*********************************************************************** GuiMultilineTextBox::CommandExecutor ***********************************************************************/ GuiMultilineTextBox::CommandExecutor::CommandExecutor(GuiMultilineTextBox* _textBox) :textBox(_textBox) { } GuiMultilineTextBox::CommandExecutor::~CommandExecutor() { } void GuiMultilineTextBox::CommandExecutor::UnsafeSetText(const WString& value) { textBox->UnsafeSetText(value); } /*********************************************************************** GuiMultilineTextBox ***********************************************************************/ void GuiMultilineTextBox::BeforeControlTemplateUninstalled_() { auto ct = GetControlTemplateObject(false); if (!ct) return; ct->SetCommands(nullptr); } void GuiMultilineTextBox::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); Array colors(1); colors[0] = ct->GetTextColor(); textElement->SetColors(colors); textElement->SetCaretColor(ct->GetCaretColor()); ct->SetCommands(commandExecutor.Obj()); } void GuiMultilineTextBox::CalculateViewAndSetScroll() { auto ct = GetControlTemplateObject(true); CalculateView(); vint smallMove = textElement->GetLines().GetRowHeight(); vint bigMove = smallMove * 5; if (auto scroll = ct->GetHorizontalScroll()) { scroll->SetSmallMove(smallMove); scroll->SetBigMove(bigMove); } if (auto scroll = ct->GetVerticalScroll()) { scroll->SetSmallMove(smallMove); scroll->SetBigMove(bigMove); } } void GuiMultilineTextBox::OnVisuallyEnabledChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { textElement->SetVisuallyEnabled(GetVisuallyEnabled()); } Size GuiMultilineTextBox::QueryFullSize() { text::TextLines& lines = textElement->GetLines(); return Size(lines.GetMaxWidth() + TextMargin * 2, lines.GetMaxHeight() + TextMargin * 2); } void GuiMultilineTextBox::UpdateView(Rect viewBounds) { textElement->SetViewPosition(viewBounds.LeftTop() - Size(TextMargin, TextMargin)); } void GuiMultilineTextBox::OnRenderTargetChanged(elements::IGuiGraphicsRenderTarget* renderTarget) { CalculateViewAndSetScroll(); GuiScrollView::OnRenderTargetChanged(renderTarget); } void GuiMultilineTextBox::OnBoundsMouseButtonDown(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { if(GetVisuallyEnabled()) { boundsComposition->GetRelatedGraphicsHost()->SetFocus(boundsComposition); } } GuiMultilineTextBox::GuiMultilineTextBox(theme::ThemeName themeName) :GuiScrollView(themeName) { textElement = GuiColorizedTextElement::Create(); textElement->SetFont(GetFont()); textComposition = new GuiBoundsComposition; textComposition->SetAlignmentToParent(Margin(0, 0, 0, 0)); textComposition->SetOwnedElement(textElement); containerComposition->AddChild(textComposition); callback = new TextElementOperatorCallback(this); commandExecutor = new CommandExecutor(this); SetFocusableComposition(boundsComposition); Install(textElement, textComposition, this, boundsComposition, focusableComposition); SetCallback(callback.Obj()); VisuallyEnabledChanged.AttachMethod(this, &GuiMultilineTextBox::OnVisuallyEnabledChanged); boundsComposition->GetEventReceiver()->leftButtonDown.AttachMethod(this, &GuiMultilineTextBox::OnBoundsMouseButtonDown); boundsComposition->GetEventReceiver()->middleButtonDown.AttachMethod(this, &GuiMultilineTextBox::OnBoundsMouseButtonDown); boundsComposition->GetEventReceiver()->rightButtonDown.AttachMethod(this, &GuiMultilineTextBox::OnBoundsMouseButtonDown); } GuiMultilineTextBox::~GuiMultilineTextBox() { } const WString& GuiMultilineTextBox::GetText() { text = textElement->GetLines().GetText(); return text; } void GuiMultilineTextBox::SetText(const WString& value) { UnsafeSetText(value); textElement->SetCaretBegin(TextPos(0, 0)); textElement->SetCaretEnd(TextPos(0, 0)); CalculateView(); } void GuiMultilineTextBox::SetFont(const FontProperties& value) { GuiControl::SetFont(value); textElement->SetFont(value); CalculateViewAndSetScroll(); } /*********************************************************************** GuiSinglelineTextBox::DefaultTextElementOperatorCallback ***********************************************************************/ GuiSinglelineTextBox::TextElementOperatorCallback::TextElementOperatorCallback(GuiSinglelineTextBox* _textControl) :GuiTextBoxCommonInterface::DefaultCallback( _textControl->textElement, _textControl->textComposition ) { } bool GuiSinglelineTextBox::TextElementOperatorCallback::BeforeModify(TextPos start, TextPos end, const WString& originalText, WString& inputText) { vint length=inputText.Length(); const wchar_t* input=inputText.Buffer(); for(vint i=0;iGetViewPosition().x; vint marginX=0; if(oldXnewX) { marginX=-TextMargin; } newX+=marginX; vint minX=-TextMargin; vint maxX=textElement->GetLines().GetMaxWidth()+TextMargin-textComposition->GetBounds().Width(); if(newX>=maxX) { newX=maxX-1; } if(newXSetViewPosition(Point(newX, -TextMargin)); } vint GuiSinglelineTextBox::TextElementOperatorCallback::GetTextMargin() { return TextMargin; } /*********************************************************************** GuiSinglelineTextBox ***********************************************************************/ void GuiSinglelineTextBox::BeforeControlTemplateUninstalled_() { } void GuiSinglelineTextBox::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); Array colors(1); colors[0] = ct->GetTextColor(); textElement->SetColors(colors); textElement->SetCaretColor(ct->GetCaretColor()); } void GuiSinglelineTextBox::RearrangeTextElement() { textCompositionTable->SetRowOption( 1, GuiCellOption::AbsoluteOption( textElement->GetLines().GetRowHeight() + 2 * TextMargin) ); } void GuiSinglelineTextBox::OnRenderTargetChanged(elements::IGuiGraphicsRenderTarget* renderTarget) { GuiControl::OnRenderTargetChanged(renderTarget); RearrangeTextElement(); } void GuiSinglelineTextBox::OnVisuallyEnabledChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { textElement->SetVisuallyEnabled(GetVisuallyEnabled()); } void GuiSinglelineTextBox::OnBoundsMouseButtonDown(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { if(GetVisuallyEnabled()) { boundsComposition->GetRelatedGraphicsHost()->SetFocus(boundsComposition); } } GuiSinglelineTextBox::GuiSinglelineTextBox(theme::ThemeName themeName) :GuiControl(themeName) { textElement = GuiColorizedTextElement::Create(); textElement->SetFont(GetFont()); textElement->SetViewPosition(Point(-GuiSinglelineTextBox::TextMargin, -GuiSinglelineTextBox::TextMargin)); textCompositionTable = new GuiTableComposition; textCompositionTable->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); textCompositionTable->SetAlignmentToParent(Margin(0, 0, 0, 0)); textCompositionTable->SetRowsAndColumns(3, 1); textCompositionTable->SetRowOption(0, GuiCellOption::PercentageOption(0.5)); textCompositionTable->SetRowOption(1, GuiCellOption::AbsoluteOption(0)); textCompositionTable->SetRowOption(2, GuiCellOption::PercentageOption(0.5)); textCompositionTable->SetColumnOption(0, GuiCellOption::PercentageOption(1.0)); containerComposition->AddChild(textCompositionTable); textComposition = new GuiCellComposition; textComposition->SetOwnedElement(textElement); textCompositionTable->AddChild(textComposition); textComposition->SetSite(1, 0, 1, 1); callback = new TextElementOperatorCallback(this); SetFocusableComposition(boundsComposition); Install(textElement, textComposition, this, boundsComposition, focusableComposition); SetCallback(callback.Obj()); VisuallyEnabledChanged.AttachMethod(this, &GuiSinglelineTextBox::OnVisuallyEnabledChanged); boundsComposition->GetEventReceiver()->leftButtonDown.AttachMethod(this, &GuiSinglelineTextBox::OnBoundsMouseButtonDown); boundsComposition->GetEventReceiver()->middleButtonDown.AttachMethod(this, &GuiSinglelineTextBox::OnBoundsMouseButtonDown); boundsComposition->GetEventReceiver()->rightButtonDown.AttachMethod(this, &GuiSinglelineTextBox::OnBoundsMouseButtonDown); } GuiSinglelineTextBox::~GuiSinglelineTextBox() { } const WString& GuiSinglelineTextBox::GetText() { text = textElement->GetLines().GetText(); return text; } void GuiSinglelineTextBox::SetText(const WString& value) { UnsafeSetText(value); textElement->SetCaretBegin(TextPos(0, 0)); textElement->SetCaretEnd(TextPos(0, 0)); } void GuiSinglelineTextBox::SetFont(const FontProperties& value) { GuiControl::SetFont(value); textElement->SetFont(value); RearrangeTextElement(); } wchar_t GuiSinglelineTextBox::GetPasswordChar() { return textElement->GetPasswordChar(); } void GuiSinglelineTextBox::SetPasswordChar(wchar_t value) { textElement->SetPasswordChar(value); } } } } /*********************************************************************** .\CONTROLS\TEMPLATES\GUIANIMATION.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace collections; using namespace reflection::description; /*********************************************************************** GuiTimedAnimation ***********************************************************************/ class GuiTimedAnimation : public Object, public virtual IGuiAnimation { protected: DateTime startTime; vuint64_t time; bool running = false; public: GuiTimedAnimation() { } ~GuiTimedAnimation() { } void Start()override { startTime = DateTime::LocalTime(); time = 0; running = true; } void Pause()override { time = GetTime(); running = false; } void Resume()override { startTime = DateTime::LocalTime(); running = true; } vuint64_t GetTime() { if (running) { return time + (DateTime::LocalTime().totalMilliseconds - startTime.totalMilliseconds); } else { return time; } } }; /*********************************************************************** GuiFiniteAnimation ***********************************************************************/ class GuiFiniteAnimation : public GuiTimedAnimation { protected: vuint64_t length = 0; Func run; public: GuiFiniteAnimation(const Func& _run, vuint64_t _length) :run(_run) , length(_length) { } ~GuiFiniteAnimation() { } void Run()override { auto currentTime = GetTime(); if (currentTime < length && run) { run(currentTime); } } bool GetStopped()override { return GetTime() >= length; } }; /*********************************************************************** GuiInfiniteAnimation ***********************************************************************/ class GuiInfiniteAnimation : public GuiTimedAnimation { protected: Func run; public: GuiInfiniteAnimation(const Func& _run) :run(_run) { } ~GuiInfiniteAnimation() { } void Run()override { if (run) { run(GetTime()); } } bool GetStopped()override { return false; } }; /*********************************************************************** IGuiAnimation ***********************************************************************/ Ptr IGuiAnimation::CreateAnimation(const Func& run, vuint64_t milliseconds) { return new GuiFiniteAnimation(run, milliseconds); } Ptr IGuiAnimation::CreateAnimation(const Func& run) { return new GuiInfiniteAnimation(run); } /*********************************************************************** IGuiAnimationCoroutine ***********************************************************************/ class GuiCoroutineAnimation : public Object, public virtual IGuiAnimationCoroutine::IImpl { protected: IGuiAnimationCoroutine::Creator creator; Ptr coroutine; Ptr waitingAnimation; vint waitingGroup = -1; Group> groupAnimations; public: GuiCoroutineAnimation(const IGuiAnimationCoroutine::Creator& _creator) :creator(_creator) { } ~GuiCoroutineAnimation() { } void OnPlayAndWait(Ptr animation)override { CHECK_ERROR(!waitingAnimation && waitingGroup == -1, L"GuiCoroutineAnimation::OnPlayAndWait(Ptr)#Cannot be called when an animation or a group has already been waiting for."); waitingAnimation = animation; waitingAnimation->Start(); } void OnPlayInGroup(Ptr animation, vint groupId)override { groupAnimations.Add(groupId, animation); animation->Start(); } void OnWaitForGroup(vint groupId)override { CHECK_ERROR(!waitingAnimation && waitingGroup == -1, L"GuiCoroutineAnimation::OnWaitForGroup(vint)#Cannot be called when an animation or a group has already been waiting for."); if (groupAnimations.Keys().Contains(groupId)) { waitingGroup = groupId; } } void Start()override { CHECK_ERROR(!coroutine, L"GuiCoroutineAnimation::Start()#Cannot be called more than once."); coroutine = creator(this); } void Pause()override { if (waitingAnimation) { waitingAnimation->Pause(); } for (vint i = 0; i < groupAnimations.Count(); i++) { FOREACH(Ptr, animation, groupAnimations.GetByIndex(i)) { animation->Pause(); } } } void Resume()override { if (waitingAnimation) { waitingAnimation->Resume(); } for (vint i = 0; i < groupAnimations.Count(); i++) { FOREACH(Ptr, animation, groupAnimations.GetByIndex(i)) { animation->Resume(); } } } void Run()override { CHECK_ERROR(coroutine, L"GuiCoroutineAnimation::Run()#Cannot be called before calling Start."); if (waitingAnimation) { waitingAnimation->Run(); if (waitingAnimation->GetStopped()) { waitingAnimation = nullptr; } } for (vint i = groupAnimations.Count() - 1; i >= 0; i--) { auto& animations = groupAnimations.GetByIndex(i); for (vint j = animations.Count() - 1; j >= 0; j--) { auto animation = animations[j]; animation->Run(); if (animation->GetStopped()) { groupAnimations.Remove(i, animation.Obj()); } } } if (waitingGroup != -1 && !groupAnimations.Keys().Contains(waitingGroup)) { waitingGroup = -1; } if (coroutine->GetStatus() == CoroutineStatus::Waiting) { if (waitingAnimation || waitingGroup != -1) { return; } coroutine->Resume(true, nullptr); } } bool GetStopped()override { if (!coroutine) return false; if (coroutine->GetStatus() != CoroutineStatus::Stopped) return false; if (waitingAnimation || groupAnimations.Count() > 0) return false; return true; } }; void IGuiAnimationCoroutine::WaitAndPause(IImpl* impl, vuint64_t milliseconds) { return PlayAndWaitAndPause(impl, IGuiAnimation::CreateAnimation({}, milliseconds)); } void IGuiAnimationCoroutine::PlayAndWaitAndPause(IImpl* impl, Ptr animation) { impl->OnPlayAndWait(animation); } void IGuiAnimationCoroutine::PlayInGroupAndPause(IImpl* impl, Ptr animation, vint groupId) { impl->OnPlayInGroup(animation, groupId); } void IGuiAnimationCoroutine::WaitForGroupAndPause(IImpl* impl, vint groupId) { impl->OnWaitForGroup(groupId); } void IGuiAnimationCoroutine::ReturnAndExit(IImpl* impl) { } Ptr IGuiAnimationCoroutine::Create(const Creator& creator) { return new GuiCoroutineAnimation(creator); } } } } /*********************************************************************** .\CONTROLS\TEXTEDITORPACKAGE\EDITORCALLBACK\GUITEXTUNDOREDO.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace elements; using namespace elements::text; using namespace compositions; /*********************************************************************** GuiGeneralUndoRedoProcessor ***********************************************************************/ GuiGeneralUndoRedoProcessor::GuiGeneralUndoRedoProcessor() :firstFutureStep(0) ,savedStep(0) ,performingUndoRedo(false) { } GuiGeneralUndoRedoProcessor::~GuiGeneralUndoRedoProcessor() { } void GuiGeneralUndoRedoProcessor::PushStep(Ptr step) { if(!performingUndoRedo) { if(firstFutureStep0) { steps.RemoveRange(firstFutureStep, count); } steps.Add(step); firstFutureStep=steps.Count(); UndoRedoChanged(); ModifiedChanged(); } } bool GuiGeneralUndoRedoProcessor::CanUndo() { return firstFutureStep>0; } bool GuiGeneralUndoRedoProcessor::CanRedo() { return steps.Count()>firstFutureStep; } void GuiGeneralUndoRedoProcessor::ClearUndoRedo() { if(!performingUndoRedo) { steps.Clear(); firstFutureStep=0; savedStep=0; } } bool GuiGeneralUndoRedoProcessor::GetModified() { return firstFutureStep!=savedStep; } void GuiGeneralUndoRedoProcessor::NotifyModificationSaved() { if(!performingUndoRedo) { savedStep=firstFutureStep; ModifiedChanged(); } } bool GuiGeneralUndoRedoProcessor::Undo() { if(!CanUndo()) return false; performingUndoRedo=true; firstFutureStep--; steps[firstFutureStep]->Undo(); performingUndoRedo=false; UndoRedoChanged(); ModifiedChanged(); return true; } bool GuiGeneralUndoRedoProcessor::Redo() { if(!CanRedo()) return false; performingUndoRedo=true; firstFutureStep++; steps[firstFutureStep-1]->Redo(); performingUndoRedo=false; UndoRedoChanged(); ModifiedChanged(); return true; } /*********************************************************************** GuiTextBoxUndoRedoProcessor::EditStep ***********************************************************************/ void GuiTextBoxUndoRedoProcessor::EditStep::Undo() { GuiTextBoxCommonInterface* ci=dynamic_cast(processor->ownerComposition->GetRelatedControl()); if(ci) { ci->Select(arguments.inputStart, arguments.inputEnd); ci->SetSelectionText(arguments.originalText); ci->Select(arguments.originalStart, arguments.originalEnd); } } void GuiTextBoxUndoRedoProcessor::EditStep::Redo() { GuiTextBoxCommonInterface* ci=dynamic_cast(processor->ownerComposition->GetRelatedControl()); if(ci) { ci->Select(arguments.originalStart, arguments.originalEnd); ci->SetSelectionText(arguments.inputText); ci->Select(arguments.inputStart, arguments.inputEnd); } } /*********************************************************************** GuiTextBoxUndoRedoProcessor ***********************************************************************/ GuiTextBoxUndoRedoProcessor::GuiTextBoxUndoRedoProcessor() :ownerComposition(0) { } GuiTextBoxUndoRedoProcessor::~GuiTextBoxUndoRedoProcessor() { } void GuiTextBoxUndoRedoProcessor::Attach(elements::GuiColorizedTextElement* element, SpinLock& elementModifyLock, compositions::GuiGraphicsComposition* _ownerComposition, vuint editVersion) { ownerComposition=_ownerComposition; } void GuiTextBoxUndoRedoProcessor::Detach() { ClearUndoRedo(); } void GuiTextBoxUndoRedoProcessor::TextEditPreview(TextEditPreviewStruct& arguments) { } void GuiTextBoxUndoRedoProcessor::TextEditNotify(const TextEditNotifyStruct& arguments) { Ptr step=new EditStep; step->processor=this; step->arguments=arguments; PushStep(step); } void GuiTextBoxUndoRedoProcessor::TextCaretChanged(const TextCaretChangedStruct& arguments) { } void GuiTextBoxUndoRedoProcessor::TextEditFinished(vuint editVersion) { } /*********************************************************************** GuiDocumentUndoRedoProcessor::ReplaceModelStep ***********************************************************************/ void GuiDocumentUndoRedoProcessor::ReplaceModelStep::Undo() { GuiDocumentCommonInterface* ci=dynamic_cast(processor->ownerComposition->GetRelatedControl()); if(ci) { ci->EditRun(arguments.inputStart, arguments.inputEnd, arguments.originalModel, true); ci->SetCaret(arguments.originalStart, arguments.originalEnd); } } void GuiDocumentUndoRedoProcessor::ReplaceModelStep::Redo() { GuiDocumentCommonInterface* ci=dynamic_cast(processor->ownerComposition->GetRelatedControl()); if(ci) { ci->EditRun(arguments.originalStart, arguments.originalEnd, arguments.inputModel, true); ci->SetCaret(arguments.inputStart, arguments.inputEnd); } } /*********************************************************************** GuiDocumentUndoRedoProcessor::RenameStyleStep ***********************************************************************/ void GuiDocumentUndoRedoProcessor::RenameStyleStep::Undo() { GuiDocumentCommonInterface* ci=dynamic_cast(processor->ownerComposition->GetRelatedControl()); if(ci) { ci->RenameStyle(arguments.newStyleName, arguments.oldStyleName); } } void GuiDocumentUndoRedoProcessor::RenameStyleStep::Redo() { GuiDocumentCommonInterface* ci=dynamic_cast(processor->ownerComposition->GetRelatedControl()); if(ci) { ci->RenameStyle(arguments.oldStyleName, arguments.newStyleName); } } /*********************************************************************** GuiDocumentUndoRedoProcessor::SetAlignmentStep ***********************************************************************/ void GuiDocumentUndoRedoProcessor::SetAlignmentStep::Undo() { GuiDocumentCommonInterface* ci=dynamic_cast(processor->ownerComposition->GetRelatedControl()); if(ci) { ci->SetParagraphAlignments(TextPos(arguments->start, 0), TextPos(arguments->end, 0), arguments->originalAlignments); } } void GuiDocumentUndoRedoProcessor::SetAlignmentStep::Redo() { GuiDocumentCommonInterface* ci=dynamic_cast(processor->ownerComposition->GetRelatedControl()); if(ci) { ci->SetParagraphAlignments(TextPos(arguments->start, 0), TextPos(arguments->end, 0), arguments->inputAlignments); } } /*********************************************************************** GuiDocumentUndoRedoProcessor ***********************************************************************/ GuiDocumentUndoRedoProcessor::GuiDocumentUndoRedoProcessor() :element(0) ,ownerComposition(0) { } GuiDocumentUndoRedoProcessor::~GuiDocumentUndoRedoProcessor() { } void GuiDocumentUndoRedoProcessor::Setup(elements::GuiDocumentElement* _element, compositions::GuiGraphicsComposition* _ownerComposition) { element=_element; ownerComposition=_ownerComposition; } void GuiDocumentUndoRedoProcessor::OnReplaceModel(const ReplaceModelStruct& arguments) { Ptr step=new ReplaceModelStep; step->processor=this; step->arguments=arguments; PushStep(step); } void GuiDocumentUndoRedoProcessor::OnRenameStyle(const RenameStyleStruct& arguments) { Ptr step=new RenameStyleStep; step->processor=this; step->arguments=arguments; PushStep(step); } void GuiDocumentUndoRedoProcessor::OnSetAlignment(Ptr arguments) { Ptr step=new SetAlignmentStep; step->processor=this; step->arguments=arguments; PushStep(step); } } } } /*********************************************************************** .\CONTROLS\TEXTEDITORPACKAGE\GUIDOCUMENTVIEWER.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace collections; using namespace elements; using namespace compositions; /*********************************************************************** GuiDocumentItem ***********************************************************************/ GuiDocumentItem::GuiDocumentItem(const WString& _name) :name(_name) { container = new GuiBoundsComposition; container->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); container->SetAssociatedCursor(GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::Arrow)); } GuiDocumentItem::~GuiDocumentItem() { if (!owned) { SafeDeleteComposition(container); } } compositions::GuiGraphicsComposition* GuiDocumentItem::GetContainer() { return container; } WString GuiDocumentItem::GetName() { return name; } /*********************************************************************** GuiDocumentCommonInterface ***********************************************************************/ void GuiDocumentCommonInterface::InvokeUndoRedoChanged() { UndoRedoChanged.Execute(documentControl->GetNotifyEventArguments()); } void GuiDocumentCommonInterface::InvokeModifiedChanged() { ModifiedChanged.Execute(documentControl->GetNotifyEventArguments()); } void GuiDocumentCommonInterface::UpdateCaretPoint() { GuiGraphicsHost* host=documentComposition->GetRelatedGraphicsHost(); if(host) { Rect caret=documentElement->GetCaretBounds(documentElement->GetCaretEnd(), documentElement->IsCaretEndPreferFrontSide()); Point view=GetDocumentViewPosition(); vint x=caret.x1-view.x; vint y=caret.y2-view.y; host->SetCaretPoint(Point(x, y), documentComposition); } } void GuiDocumentCommonInterface::Move(TextPos caret, bool shift, bool frontSide) { TextPos begin=documentElement->GetCaretBegin(); TextPos end=documentElement->GetCaretEnd(); TextPos newBegin=shift?begin:caret; TextPos newEnd=caret; documentElement->SetCaret(newBegin, newEnd, frontSide); documentElement->SetCaretVisible(true); Rect bounds=documentElement->GetCaretBounds(newEnd, frontSide); if(bounds!=Rect()) { bounds.x1-=15; bounds.y1-=15; bounds.x2+=15; bounds.y2+=15; EnsureRectVisible(bounds); } UpdateCaretPoint(); SelectionChanged.Execute(documentControl->GetNotifyEventArguments()); } bool GuiDocumentCommonInterface::ProcessKey(vint code, bool shift, bool ctrl) { if(IGuiShortcutKeyItem* item=internalShortcutKeyManager->TryGetShortcut(ctrl, shift, false, code)) { GuiEventArgs arguments; item->Executed.Execute(arguments); return true; } TextPos currentCaret=documentElement->GetCaretEnd(); bool frontSide=documentElement->IsCaretEndPreferFrontSide(); TextPos begin=documentElement->GetCaretBegin(); TextPos end=documentElement->GetCaretEnd(); switch(code) { case VKEY_UP: { TextPos newCaret=documentElement->CalculateCaret(currentCaret, IGuiGraphicsParagraph::CaretMoveUp, frontSide); Move(newCaret, shift, frontSide); } break; case VKEY_DOWN: { TextPos newCaret=documentElement->CalculateCaret(currentCaret, IGuiGraphicsParagraph::CaretMoveDown, frontSide); Move(newCaret, shift, frontSide); } break; case VKEY_LEFT: { TextPos newCaret=documentElement->CalculateCaret(currentCaret, IGuiGraphicsParagraph::CaretMoveLeft, frontSide); Move(newCaret, shift, frontSide); } break; case VKEY_RIGHT: { TextPos newCaret=documentElement->CalculateCaret(currentCaret, IGuiGraphicsParagraph::CaretMoveRight, frontSide); Move(newCaret, shift, frontSide); } break; case VKEY_HOME: { TextPos newCaret=documentElement->CalculateCaret(currentCaret, IGuiGraphicsParagraph::CaretLineFirst, frontSide); if(newCaret==currentCaret) { newCaret=documentElement->CalculateCaret(currentCaret, IGuiGraphicsParagraph::CaretFirst, frontSide); } Move(newCaret, shift, frontSide); } break; case VKEY_END: { TextPos newCaret=documentElement->CalculateCaret(currentCaret, IGuiGraphicsParagraph::CaretLineLast, frontSide); if(newCaret==currentCaret) { newCaret=documentElement->CalculateCaret(currentCaret, IGuiGraphicsParagraph::CaretLast, frontSide); } Move(newCaret, shift, frontSide); } break; case VKEY_PRIOR: { } break; case VKEY_NEXT: { } break; case VKEY_BACK: if(editMode==Editable) { if(begin==end) { ProcessKey(VKEY_LEFT, true, false); } Array text; EditText(documentElement->GetCaretBegin(), documentElement->GetCaretEnd(), documentElement->IsCaretEndPreferFrontSide(), text); return true; } break; case VKEY_DELETE: if(editMode==Editable) { if(begin==end) { ProcessKey(VKEY_RIGHT, true, false); } Array text; EditText(documentElement->GetCaretBegin(), documentElement->GetCaretEnd(), documentElement->IsCaretEndPreferFrontSide(), text); return true; } break; case VKEY_RETURN: if(editMode==Editable) { if(ctrl) { Array text(1); text[0]=L"\r\n"; EditText(documentElement->GetCaretBegin(), documentElement->GetCaretEnd(), documentElement->IsCaretEndPreferFrontSide(), text); } else { Array text(2); EditText(documentElement->GetCaretBegin(), documentElement->GetCaretEnd(), documentElement->IsCaretEndPreferFrontSide(), text); } return true; } break; } return false; } void GuiDocumentCommonInterface::InstallDocumentViewer( GuiControl* _sender, compositions::GuiGraphicsComposition* _container, compositions::GuiGraphicsComposition* eventComposition, compositions::GuiGraphicsComposition* focusableComposition ) { documentControl=_sender; documentElement=GuiDocumentElement::Create(); documentElement->SetCallback(this); documentComposition=new GuiBoundsComposition; documentComposition->SetOwnedElement(documentElement); documentComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElement); documentComposition->SetAlignmentToParent(Margin(5, 5, 5, 5)); _container->AddChild(documentComposition); documentComposition->GetEventReceiver()->mouseMove.AttachMethod(this, &GuiDocumentCommonInterface::OnMouseMove); documentComposition->GetEventReceiver()->leftButtonDown.AttachMethod(this, &GuiDocumentCommonInterface::OnMouseDown); documentComposition->GetEventReceiver()->leftButtonUp.AttachMethod(this, &GuiDocumentCommonInterface::OnMouseUp); documentComposition->GetEventReceiver()->mouseLeave.AttachMethod(this, &GuiDocumentCommonInterface::OnMouseLeave); _sender->FontChanged.AttachMethod(this, &GuiDocumentCommonInterface::OnFontChanged); focusableComposition->GetEventReceiver()->caretNotify.AttachMethod(this, &GuiDocumentCommonInterface::OnCaretNotify); focusableComposition->GetEventReceiver()->gotFocus.AttachMethod(this, &GuiDocumentCommonInterface::OnGotFocus); focusableComposition->GetEventReceiver()->lostFocus.AttachMethod(this, &GuiDocumentCommonInterface::OnLostFocus); focusableComposition->GetEventReceiver()->keyDown.AttachMethod(this, &GuiDocumentCommonInterface::OnKeyDown); focusableComposition->GetEventReceiver()->charInput.AttachMethod(this, &GuiDocumentCommonInterface::OnCharInput); undoRedoProcessor->Setup(documentElement, documentComposition); ActiveHyperlinkChanged.SetAssociatedComposition(eventComposition); ActiveHyperlinkExecuted.SetAssociatedComposition(eventComposition); SelectionChanged.SetAssociatedComposition(eventComposition); UndoRedoChanged.SetAssociatedComposition(eventComposition); ModifiedChanged.SetAssociatedComposition(eventComposition); undoRedoProcessor->UndoRedoChanged.Add(this, &GuiDocumentCommonInterface::InvokeUndoRedoChanged); undoRedoProcessor->ModifiedChanged.Add(this, &GuiDocumentCommonInterface::InvokeModifiedChanged); SetDocument(new DocumentModel); } void GuiDocumentCommonInterface::SetActiveHyperlink(Ptr package) { ActivateActiveHyperlink(false); activeHyperlinks = !package ? nullptr : package->hyperlinks.Count() == 0 ? nullptr : package; ActivateActiveHyperlink(true); ActiveHyperlinkChanged.Execute(documentControl->GetNotifyEventArguments()); } void GuiDocumentCommonInterface::ActivateActiveHyperlink(bool activate) { if (activeHyperlinks) { FOREACH(Ptr, run, activeHyperlinks->hyperlinks) { run->styleName = activate ? run->activeStyleName : run->normalStyleName; } documentElement->NotifyParagraphUpdated(activeHyperlinks->row, 1, 1, false); } } void GuiDocumentCommonInterface::AddShortcutCommand(vint key, const Func& eventHandler) { IGuiShortcutKeyItem* item=internalShortcutKeyManager->CreateShortcut(true, false, false, key); item->Executed.AttachLambda([=](GuiGraphicsComposition* sender, GuiEventArgs& arguments) { eventHandler(); }); } void GuiDocumentCommonInterface::EditTextInternal(TextPos begin, TextPos end, const Func& editor) { // save run before editing if(begin>end) { TextPos temp=begin; begin=end; end=temp; } Ptr originalModel=documentElement->GetDocument()->CopyDocument(begin, end, true); if(originalModel) { // edit vint paragraphCount=0; vint lastParagraphLength=0; editor(begin, end, paragraphCount, lastParagraphLength); // calculate new caret TextPos caret; if(paragraphCount==0) { caret=begin; } else if(paragraphCount==1) { caret=TextPos(begin.row, begin.column+lastParagraphLength); } else { caret=TextPos(begin.row+paragraphCount-1, lastParagraphLength); } documentElement->SetCaret(caret, caret, true); documentControl->TextChanged.Execute(documentControl->GetNotifyEventArguments()); UpdateCaretPoint(); SelectionChanged.Execute(documentControl->GetNotifyEventArguments()); // save run after editing Ptr inputModel=documentElement->GetDocument()->CopyDocument(begin, caret, true); // submit redo-undo GuiDocumentUndoRedoProcessor::ReplaceModelStruct arguments; arguments.originalStart=begin; arguments.originalEnd=end; arguments.originalModel=originalModel; arguments.inputStart=begin; arguments.inputEnd=caret; arguments.inputModel=inputModel; undoRedoProcessor->OnReplaceModel(arguments); } } void GuiDocumentCommonInterface::EditStyleInternal(TextPos begin, TextPos end, const Func& editor) { // save run before editing if(begin>end) { TextPos temp=begin; begin=end; end=temp; } Ptr originalModel=documentElement->GetDocument()->CopyDocument(begin, end, true); if(originalModel) { // edit editor(begin, end); // save run after editing Ptr inputModel=documentElement->GetDocument()->CopyDocument(begin, end, true); // submit redo-undo GuiDocumentUndoRedoProcessor::ReplaceModelStruct arguments; arguments.originalStart=begin; arguments.originalEnd=end; arguments.originalModel=originalModel; arguments.inputStart=begin; arguments.inputEnd=end; arguments.inputModel=inputModel; undoRedoProcessor->OnReplaceModel(arguments); } } void GuiDocumentCommonInterface::MergeBaselineAndDefaultFont(Ptr document) { document->MergeDefaultFont(documentControl->GetFont()); if (baselineDocument) { document->MergeBaselineStyles(baselineDocument); } } void GuiDocumentCommonInterface::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { auto document = documentElement->GetDocument(); MergeBaselineAndDefaultFont(document); documentElement->SetDocument(document); } void GuiDocumentCommonInterface::OnCaretNotify(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if(documentControl->GetVisuallyEnabled()) { if(editMode!=ViewOnly) { documentElement->SetCaretVisible(!documentElement->GetCaretVisible()); } } } void GuiDocumentCommonInterface::OnGotFocus(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if(documentControl->GetVisuallyEnabled()) { if(editMode!=ViewOnly) { documentElement->SetCaretVisible(true); UpdateCaretPoint(); } } } void GuiDocumentCommonInterface::OnLostFocus(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if(documentControl->GetVisuallyEnabled()) { documentElement->SetCaretVisible(false); } } void GuiDocumentCommonInterface::OnKeyDown(compositions::GuiGraphicsComposition* sender, compositions::GuiKeyEventArgs& arguments) { if(documentControl->GetVisuallyEnabled()) { if(editMode!=ViewOnly) { if(ProcessKey(arguments.code, arguments.shift, arguments.ctrl)) { arguments.handled=true; } } } } void GuiDocumentCommonInterface::OnCharInput(compositions::GuiGraphicsComposition* sender, compositions::GuiCharEventArgs& arguments) { if(documentControl->GetVisuallyEnabled()) { if(editMode==Editable && arguments.code!=VKEY_ESCAPE && arguments.code!=VKEY_BACK && arguments.code!=VKEY_RETURN && !arguments.ctrl) { Array text(1); text[0]=WString(arguments.code); EditText(documentElement->GetCaretBegin(), documentElement->GetCaretEnd(), documentElement->IsCaretEndPreferFrontSide(), text); } } } void GuiDocumentCommonInterface::OnMouseMove(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments) { if(documentControl->GetVisuallyEnabled()) { switch(editMode) { case ViewOnly: { auto package = documentElement->GetHyperlinkFromPoint({ arguments.x, arguments.y }); bool handCursor = false; if(dragging) { if(activeHyperlinks) { if (package && CompareEnumerable(activeHyperlinks->hyperlinks, package->hyperlinks) == 0) { ActivateActiveHyperlink(true); handCursor = true; } else { ActivateActiveHyperlink(false); } } } else { SetActiveHyperlink(package); handCursor = activeHyperlinks; } if(handCursor) { auto cursor = GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::Hand); documentComposition->SetAssociatedCursor(cursor); } else { documentComposition->SetAssociatedCursor(nullptr); } } break; case Selectable: case Editable: if(dragging) { TextPos caret=documentElement->CalculateCaretFromPoint(Point(arguments.x, arguments.y)); TextPos oldCaret=documentElement->GetCaretBegin(); Move(caret, true, (oldCaret==caret?documentElement->IsCaretEndPreferFrontSide():caretGetVisuallyEnabled()) { documentControl->SetFocus(); switch(editMode) { case ViewOnly: SetActiveHyperlink(documentElement->GetHyperlinkFromPoint({ arguments.x, arguments.y })); break; case Selectable: case Editable: { TextPos caret=documentElement->CalculateCaretFromPoint(Point(arguments.x, arguments.y)); TextPos oldCaret=documentElement->GetCaretEnd(); if(caret!=oldCaret) { Move(caret, arguments.shift, caretGetVisuallyEnabled()) { dragging=false; switch(editMode) { case ViewOnly: { auto package = documentElement->GetHyperlinkFromPoint({ arguments.x, arguments.y }); if(activeHyperlinks) { if (package && CompareEnumerable(activeHyperlinks->hyperlinks, package->hyperlinks) == 0) { ActiveHyperlinkExecuted.Execute(documentControl->GetNotifyEventArguments()); } else { SetActiveHyperlink(nullptr); } } } break; default:; } } } void GuiDocumentCommonInterface::OnMouseLeave(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { SetActiveHyperlink(0); } Point GuiDocumentCommonInterface::GetDocumentViewPosition() { return Point(0, 0); } void GuiDocumentCommonInterface::EnsureRectVisible(Rect bounds) { } //================ callback void GuiDocumentCommonInterface::OnStartRender() { FOREACH(Ptr, item, documentItems.Values()) { item->visible = false; } } void GuiDocumentCommonInterface::OnFinishRender() { FOREACH(Ptr, item, documentItems.Values()) { if (item->container->GetVisible() != item->visible) { item->container->SetVisible(item->visible); } } } Size GuiDocumentCommonInterface::OnRenderEmbeddedObject(const WString& name, const Rect& location) { vint index = documentItems.Keys().IndexOf(name); if (index != -1) { auto item = documentItems.Values()[index]; auto size = item->container->GetBounds().GetSize(); item->container->SetBounds(Rect(location.LeftTop(), Size(0, 0))); item->visible = true; return size; } return Size(); } //================ basic GuiDocumentCommonInterface::GuiDocumentCommonInterface() { undoRedoProcessor=new GuiDocumentUndoRedoProcessor; internalShortcutKeyManager=new GuiShortcutKeyManager; AddShortcutCommand('Z', Func(this, &GuiDocumentCommonInterface::Undo)); AddShortcutCommand('Y', Func(this, &GuiDocumentCommonInterface::Redo)); AddShortcutCommand('A', Func(this, &GuiDocumentCommonInterface::SelectAll)); AddShortcutCommand('X', Func(this, &GuiDocumentCommonInterface::Cut)); AddShortcutCommand('C', Func(this, &GuiDocumentCommonInterface::Copy)); AddShortcutCommand('V', Func(this, &GuiDocumentCommonInterface::Paste)); } GuiDocumentCommonInterface::~GuiDocumentCommonInterface() { } Ptr GuiDocumentCommonInterface::GetDocument() { return documentElement->GetDocument(); } void GuiDocumentCommonInterface::SetDocument(Ptr value) { SetActiveHyperlink(0); ClearUndoRedo(); NotifyModificationSaved(); if (value) { if (value->paragraphs.Count() == 0) { value->paragraphs.Add(new DocumentParagraphRun); } MergeBaselineAndDefaultFont(value); } documentElement->SetDocument(value); } //================ document items bool GuiDocumentCommonInterface::AddDocumentItem(Ptr value) { if (documentItems.Keys().Contains(value->GetName())) { return false; } documentItems.Add(value->GetName(), value); documentComposition->AddChild(value->container); value->visible = false; value->owned = true; value->container->SetVisible(false); return true; } bool GuiDocumentCommonInterface::RemoveDocumentItem(Ptr value) { vint index = documentItems.Keys().IndexOf(value->GetName()); if (index == -1) { return false; } if (documentItems.Values()[index] != value) { return false; } value->owned = false; documentComposition->RemoveChild(value->container); documentItems.Remove(value->GetName()); return true; } const GuiDocumentCommonInterface::DocumentItemMap& GuiDocumentCommonInterface::GetDocumentItems() { return documentItems; } //================ caret operations TextPos GuiDocumentCommonInterface::GetCaretBegin() { return documentElement->GetCaretBegin(); } TextPos GuiDocumentCommonInterface::GetCaretEnd() { return documentElement->GetCaretEnd(); } void GuiDocumentCommonInterface::SetCaret(TextPos begin, TextPos end) { documentElement->SetCaret(begin, end, end>=begin); UpdateCaretPoint(); SelectionChanged.Execute(documentControl->GetNotifyEventArguments()); } TextPos GuiDocumentCommonInterface::CalculateCaretFromPoint(Point point) { return documentElement->CalculateCaretFromPoint(point); } Rect GuiDocumentCommonInterface::GetCaretBounds(TextPos caret, bool frontSide) { return documentElement->GetCaretBounds(caret, frontSide); } //================ editing operations void GuiDocumentCommonInterface::NotifyParagraphUpdated(vint index, vint oldCount, vint newCount, bool updatedText) { documentElement->NotifyParagraphUpdated(index, oldCount, newCount, updatedText); } void GuiDocumentCommonInterface::EditRun(TextPos begin, TextPos end, Ptr model, bool copy) { EditTextInternal(begin, end, [=](TextPos begin, TextPos end, vint& paragraphCount, vint& lastParagraphLength) { documentElement->EditRun(begin, end, model, copy); paragraphCount=model->paragraphs.Count(); lastParagraphLength=paragraphCount==0?0:model->paragraphs[paragraphCount-1]->GetText(false).Length(); }); } void GuiDocumentCommonInterface::EditText(TextPos begin, TextPos end, bool frontSide, const collections::Array& text) { EditTextInternal(begin, end, [=, &text](TextPos begin, TextPos end, vint& paragraphCount, vint& lastParagraphLength) { documentElement->EditText(begin, end, frontSide, text); paragraphCount=text.Count(); lastParagraphLength=paragraphCount==0?0:text[paragraphCount-1].Length(); }); } void GuiDocumentCommonInterface::EditStyle(TextPos begin, TextPos end, Ptr style) { EditStyleInternal(begin, end, [=](TextPos begin, TextPos end) { documentElement->EditStyle(begin, end, style); }); } void GuiDocumentCommonInterface::EditImage(TextPos begin, TextPos end, Ptr image) { EditTextInternal(begin, end, [=](TextPos begin, TextPos end, vint& paragraphCount, vint& lastParagraphLength) { documentElement->EditImage(begin, end, image); paragraphCount=1; lastParagraphLength=wcslen(DocumentImageRun::RepresentationText); }); } void GuiDocumentCommonInterface::EditHyperlink(vint paragraphIndex, vint begin, vint end, const WString& reference, const WString& normalStyleName, const WString& activeStyleName) { EditStyleInternal(TextPos(paragraphIndex, begin), TextPos(paragraphIndex, end), [=](TextPos begin, TextPos end) { documentElement->EditHyperlink(begin.row, begin.column, end.column, reference, normalStyleName, activeStyleName); }); } void GuiDocumentCommonInterface::RemoveHyperlink(vint paragraphIndex, vint begin, vint end) { EditStyleInternal(TextPos(paragraphIndex, begin), TextPos(paragraphIndex, end), [=](TextPos begin, TextPos end) { documentElement->RemoveHyperlink(begin.row, begin.column, end.column); }); } void GuiDocumentCommonInterface::EditStyleName(TextPos begin, TextPos end, const WString& styleName) { EditStyleInternal(begin, end, [=](TextPos begin, TextPos end) { documentElement->EditStyleName(begin, end, styleName); }); } void GuiDocumentCommonInterface::RemoveStyleName(TextPos begin, TextPos end) { EditStyleInternal(begin, end, [=](TextPos begin, TextPos end) { documentElement->RemoveStyleName(begin, end); }); } void GuiDocumentCommonInterface::RenameStyle(const WString& oldStyleName, const WString& newStyleName) { documentElement->RenameStyle(oldStyleName, newStyleName); // submit redo-undo GuiDocumentUndoRedoProcessor::RenameStyleStruct arguments; arguments.oldStyleName=oldStyleName; arguments.newStyleName=newStyleName; undoRedoProcessor->OnRenameStyle(arguments); } void GuiDocumentCommonInterface::ClearStyle(TextPos begin, TextPos end) { EditStyleInternal(begin, end, [=](TextPos begin, TextPos end) { documentElement->ClearStyle(begin, end); }); } Ptr GuiDocumentCommonInterface::SummarizeStyle(TextPos begin, TextPos end) { if (begin>end) { TextPos temp = begin; begin = end; end = temp; } return documentElement->SummarizeStyle(begin, end); } Nullable GuiDocumentCommonInterface::SummarizeStyleName(TextPos begin, TextPos end) { if (begin>end) { TextPos temp = begin; begin = end; end = temp; } return documentElement->SummarizeStyleName(begin, end); } void GuiDocumentCommonInterface::SetParagraphAlignments(TextPos begin, TextPos end, const collections::Array>& alignments) { vint first = begin.row; vint last = end.row; if (first > last) { vint temp = first; first = last; last = temp; } Ptr document = documentElement->GetDocument(); if (0 <= first && first < document->paragraphs.Count() && 0 <= last && last < document->paragraphs.Count() && last - first + 1 == alignments.Count()) { Ptr arguments = new GuiDocumentUndoRedoProcessor::SetAlignmentStruct; arguments->start = first; arguments->end = last; arguments->originalAlignments.Resize(alignments.Count()); arguments->inputAlignments.Resize(alignments.Count()); for (vint i = first; i <= last; i++) { arguments->originalAlignments[i - first] = document->paragraphs[i]->alignment; arguments->inputAlignments[i - first] = alignments[i - first]; } documentElement->SetParagraphAlignment(begin, end, alignments); undoRedoProcessor->OnSetAlignment(arguments); } } void GuiDocumentCommonInterface::SetParagraphAlignment(TextPos begin, TextPos end, Nullable alignment) { #if defined VCZH_GCC && defined VCZH_64 #define abs labs #endif Array> alignments(abs(begin.row - end.row) + 1); #if defined VCZH_GCC && defined VCZH_64 #undef abs #endif for (vint i = 0; i < alignments.Count(); i++) { alignments[i] = alignment; } SetParagraphAlignments(begin, end, alignments); } Nullable GuiDocumentCommonInterface::SummarizeParagraphAlignment(TextPos begin, TextPos end) { if (begin>end) { TextPos temp = begin; begin = end; end = temp; } return documentElement->SummarizeParagraphAlignment(begin, end); } //================ editing control WString GuiDocumentCommonInterface::GetActiveHyperlinkReference() { return activeHyperlinks ? activeHyperlinks->hyperlinks[0]->reference : L""; } GuiDocumentCommonInterface::EditMode GuiDocumentCommonInterface::GetEditMode() { return editMode; } void GuiDocumentCommonInterface::SetEditMode(EditMode value) { if(activeHyperlinks) { SetActiveHyperlink(nullptr); } editMode=value; if(editMode==ViewOnly) { documentComposition->SetAssociatedCursor(0); } else { INativeCursor* cursor=GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::IBeam); documentComposition->SetAssociatedCursor(cursor); } } //================ selection operations void GuiDocumentCommonInterface::SelectAll() { vint lastIndex=documentElement->GetDocument()->paragraphs.Count()-1; Ptr lastParagraph=documentElement->GetDocument()->paragraphs[lastIndex]; TextPos begin(0, 0); TextPos end(lastIndex, lastParagraph->GetText(false).Length()); SetCaret(begin, end); } WString GuiDocumentCommonInterface::GetSelectionText() { TextPos begin=documentElement->GetCaretBegin(); TextPos end=documentElement->GetCaretEnd(); if(begin>end) { TextPos temp=begin; begin=end; end=temp; } Ptr model=documentElement->GetDocument()->CopyDocument(begin, end, false); return model->GetText(true); } void GuiDocumentCommonInterface::SetSelectionText(const WString& value) { List paragraphs; { stream::StringReader reader(value); WString paragraph; bool empty=true; while(!reader.IsEnd()) { WString line=reader.ReadLine(); if(empty) { paragraph+=line; empty=false; } else if(line!=L"") { paragraph+=L"\r\n"+line; } else { paragraphs.Add(paragraph); paragraph=L""; empty=true; } } if(!empty) { paragraphs.Add(paragraph); } } TextPos begin=documentElement->GetCaretBegin(); TextPos end=documentElement->GetCaretEnd(); if(begin>end) { TextPos temp=begin; begin=end; end=temp; } Array text; CopyFrom(text, paragraphs); EditText(begin, end, documentElement->IsCaretEndPreferFrontSide(), text); } Ptr GuiDocumentCommonInterface::GetSelectionModel() { TextPos begin=documentElement->GetCaretBegin(); TextPos end=documentElement->GetCaretEnd(); if(begin>end) { TextPos temp=begin; begin=end; end=temp; } Ptr model=documentElement->GetDocument()->CopyDocument(begin, end, true); return model; } void GuiDocumentCommonInterface::SetSelectionModel(Ptr value) { TextPos begin=documentElement->GetCaretBegin(); TextPos end=documentElement->GetCaretEnd(); if(begin>end) { TextPos temp=begin; begin=end; end=temp; } EditRun(begin, end, value, true); } //================ clipboard operations bool GuiDocumentCommonInterface::CanCut() { return editMode==Editable && documentElement->GetCaretBegin()!=documentElement->GetCaretEnd(); } bool GuiDocumentCommonInterface::CanCopy() { return documentElement->GetCaretBegin()!=documentElement->GetCaretEnd(); } bool GuiDocumentCommonInterface::CanPaste() { return editMode==Editable && GetCurrentController()->ClipboardService()->ContainsText(); } bool GuiDocumentCommonInterface::Cut() { if(CanCut()) { GetCurrentController()->ClipboardService()->SetText(GetSelectionText()); SetSelectionText(L""); return true; } else { return false; } } bool GuiDocumentCommonInterface::Copy() { if(CanCopy()) { GetCurrentController()->ClipboardService()->SetText(GetSelectionText()); return true; } else { return false; } } bool GuiDocumentCommonInterface::Paste() { if(CanPaste()) { SetSelectionText(GetCurrentController()->ClipboardService()->GetText()); return true; } else { return false; } } //================ undo redo control bool GuiDocumentCommonInterface::CanUndo() { return editMode==Editable && undoRedoProcessor->CanUndo(); } bool GuiDocumentCommonInterface::CanRedo() { return editMode==Editable && undoRedoProcessor->CanRedo(); } void GuiDocumentCommonInterface::ClearUndoRedo() { undoRedoProcessor->ClearUndoRedo(); } bool GuiDocumentCommonInterface::GetModified() { return undoRedoProcessor->GetModified(); } void GuiDocumentCommonInterface::NotifyModificationSaved() { undoRedoProcessor->NotifyModificationSaved(); } bool GuiDocumentCommonInterface::Undo() { if(CanUndo()) { return undoRedoProcessor->Undo(); } else { return false; } } bool GuiDocumentCommonInterface::Redo() { if(CanRedo()) { return undoRedoProcessor->Redo(); } else { return false; } } /*********************************************************************** GuiDocumentViewer ***********************************************************************/ void GuiDocumentViewer::BeforeControlTemplateUninstalled_() { } void GuiDocumentViewer::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); baselineDocument = ct->GetBaselineDocument(); if (documentElement) { documentElement->SetCaretColor(ct->GetCaretColor()); SetDocument(GetDocument()); } } Point GuiDocumentViewer::GetDocumentViewPosition() { return GetViewBounds().LeftTop(); } void GuiDocumentViewer::EnsureRectVisible(Rect bounds) { Rect viewBounds=GetViewBounds(); vint offset=0; if(bounds.y1viewBounds.y2) { offset=bounds.y2-viewBounds.y2; } if (auto scroll = GetVerticalScroll()) { scroll->SetPosition(viewBounds.y1 + offset); } } GuiDocumentViewer::GuiDocumentViewer(theme::ThemeName themeName) :GuiScrollContainer(themeName) { SetFocusableComposition(boundsComposition); InstallDocumentViewer(this, containerComposition, boundsComposition, focusableComposition); SetExtendToFullWidth(true); SetHorizontalAlwaysVisible(false); } GuiDocumentViewer::~GuiDocumentViewer() { } const WString& GuiDocumentViewer::GetText() { text=documentElement->GetDocument()->GetText(true); return text; } void GuiDocumentViewer::SetText(const WString& value) { SelectAll(); SetSelectionText(value); } /*********************************************************************** GuiDocumentLabel ***********************************************************************/ void GuiDocumentLabel::BeforeControlTemplateUninstalled_() { } void GuiDocumentLabel::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); baselineDocument = ct->GetBaselineDocument(); if (documentElement) { documentElement->SetCaretColor(ct->GetCaretColor()); SetDocument(GetDocument()); } } GuiDocumentLabel::GuiDocumentLabel(theme::ThemeName themeName) :GuiControl(themeName) { SetFocusableComposition(boundsComposition); InstallDocumentViewer(this, containerComposition, boundsComposition, focusableComposition); } GuiDocumentLabel::~GuiDocumentLabel() { } const WString& GuiDocumentLabel::GetText() { text=documentElement->GetDocument()->GetText(true); return text; } void GuiDocumentLabel::SetText(const WString& value) { SelectAll(); SetSelectionText(value); } } } } /*********************************************************************** .\CONTROLS\TEXTEDITORPACKAGE\LANGUAGESERVICE\GUILANGUAGEAUTOCOMPLETE.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace elements; using namespace regex; using namespace parsing; using namespace parsing::tabling; using namespace collections; /*********************************************************************** GuiGrammarAutoComplete ***********************************************************************/ void GuiGrammarAutoComplete::Attach(elements::GuiColorizedTextElement* _element, SpinLock& _elementModifyLock, compositions::GuiGraphicsComposition* _ownerComposition, vuint editVersion) { GuiTextBoxAutoCompleteBase::Attach(_element, _elementModifyLock, _ownerComposition, editVersion); RepeatingParsingExecutor::CallbackBase::Attach(_element, _elementModifyLock, _ownerComposition, editVersion); } void GuiGrammarAutoComplete::Detach() { GuiTextBoxAutoCompleteBase::Detach(); RepeatingParsingExecutor::CallbackBase::Detach(); if(element && elementModifyLock) { EnsureAutoCompleteFinished(); } } void GuiGrammarAutoComplete::TextEditPreview(TextEditPreviewStruct& arguments) { GuiTextBoxAutoCompleteBase::TextEditPreview(arguments); RepeatingParsingExecutor::CallbackBase::TextEditPreview(arguments); if(element && elementModifyLock) { if(IsListOpening() && arguments.keyInput && arguments.originalText==L"" && arguments.inputText!=L"") { WString selectedItem=GetSelectedListItem(); if(selectedItem!=L"") { TextPos begin=GetListStartPosition(); TextPos end=arguments.originalStart; WString editingText=element->GetLines().GetText(begin, end); editingText+=arguments.inputText; if(grammarParser->GetTable()->GetLexer().Walk().IsClosedToken(editingText)) { arguments.originalStart=begin; arguments.inputText=selectedItem+arguments.inputText; CloseList(); } } } } } void GuiGrammarAutoComplete::TextEditNotify(const TextEditNotifyStruct& arguments) { GuiTextBoxAutoCompleteBase::TextEditNotify(arguments); RepeatingParsingExecutor::CallbackBase::TextEditNotify(arguments); if(element && elementModifyLock) { editing=true; SPIN_LOCK(editTraceLock) { editTrace.Add(arguments); } } } void GuiGrammarAutoComplete::TextCaretChanged(const TextCaretChangedStruct& arguments) { GuiTextBoxAutoCompleteBase::TextCaretChanged(arguments); RepeatingParsingExecutor::CallbackBase::TextCaretChanged(arguments); if(element && elementModifyLock) { SPIN_LOCK(editTraceLock) { // queue a fake TextEditNotifyStruct // a fake struct can be detected by (trace.originalText==L"" && trace.inputText==L"") TextEditNotifyStruct trace; trace.editVersion=arguments.editVersion; trace.originalStart=arguments.oldBegin; trace.originalEnd=arguments.oldEnd; trace.inputStart=arguments.newBegin; trace.inputEnd=arguments.newEnd; // ensure trace.originalStart<=trace.originalEnd if(trace.originalStart>trace.originalEnd) { TextPos temp=trace.originalStart; trace.originalStart=trace.originalEnd; trace.originalEnd=temp; } // ensure trace.inputStart<=trace.inputEnd if(trace.inputStart>trace.inputEnd) { TextPos temp=trace.inputStart; trace.inputStart=trace.inputEnd; trace.inputEnd=temp; } editTrace.Add(trace); } SPIN_LOCK(contextLock) { if(context.input.node) { if(editing) { // if the current caret changing is caused by editing // submit a task with valid editVersion and invalid node and code RepeatingParsingOutput input; input.editVersion=context.input.editVersion; SubmitTask(input); } else if(context.input.editVersion == arguments.editVersion) { // if the current caret changing is not caused by editing // submit a task with the previous input SubmitTask(context.input); } } } } } void GuiGrammarAutoComplete::TextEditFinished(vuint editVersion) { GuiTextBoxAutoCompleteBase::TextEditFinished(editVersion); RepeatingParsingExecutor::CallbackBase::TextEditFinished(editVersion); if(element && elementModifyLock) { editing=false; } } void GuiGrammarAutoComplete::OnParsingFinishedAsync(const RepeatingParsingOutput& arguments) { if(element && elementModifyLock) { GetApplication()->InvokeInMainThread(ownerComposition->GetRelatedControlHost(), [=]() { // submit a task if the RepeatingParsingExecutor notices a new parsing result SubmitTask(arguments); }); } } void GuiGrammarAutoComplete::CollectLeftRecursiveRules() { leftRecursiveRules.Clear(); Ptr parser=parsingExecutor->GetParser(); Ptr table=parser->GetTable(); vint stateCount=table->GetStateCount(); vint tokenCount=table->GetTokenCount(); for(vint i=0;i bag=table->GetTransitionBag(i, j); if(bag) { FOREACH(Ptr, item, bag->transitionItems) { FOREACH(ParsingTable::Instruction, ins, item->instructions) { if(ins.instructionType==ParsingTable::Instruction::LeftRecursiveReduce) { if(!leftRecursiveRules.Contains(ins.creatorRule)) { leftRecursiveRules.Add(ins.creatorRule); } } } } } } } } vint GuiGrammarAutoComplete::UnsafeGetEditTraceIndex(vuint editVersion) { // get the index of the latest TextEditNotifyStruct of a specified edit version // this function should be called inside SPIN_LOCK(editTraceLock) // perform a binary search vint start = 0; vint end = editTrace.Count() - 1; while (start <= end) { vint middle = (start + end) / 2; TextEditNotifyStruct& trace = editTrace[middle]; if (editVersiontrace.editVersion) { start = middle + 1; } else { // if multiple TextEditNotifyStruct is found, choose the latest one while (middle < editTrace.Count() - 1) { if (editTrace[middle + 1].editVersion == editTrace[middle].editVersion) { middle++; } else { break; } } return middle; } } return -1; } TextPos GuiGrammarAutoComplete::ChooseCorrectTextPos(TextPos pos, const regex::RegexTokens& tokens) { Ptr table=grammarParser->GetTable(); RegexToken lastToken; lastToken.reading=0; FOREACH(RegexToken, token, tokens) { // we treat "class| Name" as editing the first token if(TextPos(token.rowEnd, token.columnEnd+1)>=pos) { if(table->GetTableTokenIndex(token.token)!=-1 && lastToken.reading) { pos=TextPos(lastToken.rowStart, lastToken.columnStart); } break; } lastToken=token; } return pos; } void GuiGrammarAutoComplete::ExecuteRefresh(AutoCompleteContext& newContext) { // process the input of a task is submitted not by text editing // find the text selection by the edit version of the input TextPos startPos, endPos; { SPIN_LOCK(editTraceLock) { vint traceIndex = UnsafeGetEditTraceIndex(newContext.input.editVersion); if (traceIndex == -1) return; TextEditNotifyStruct& trace = editTrace[traceIndex]; startPos = trace.inputStart; endPos = trace.inputEnd; } const RegexLexer& lexer = grammarParser->GetTable()->GetLexer(); RegexTokens tokens = lexer.Parse(newContext.input.code); startPos = ChooseCorrectTextPos(startPos, tokens); } // locate the deepest node using the text selection ParsingTextPos start(startPos.row, startPos.column); ParsingTextPos end(endPos.row, endPos.column); ParsingTextRange range(start, end); ParsingTreeNode* found = newContext.input.node->FindDeepestNode(range); ParsingTreeObject* selectedNode = 0; // if the location failed, choose the root node if (!found || startPos == TextPos(0, 0)) { found = newContext.input.node.Obj(); } if (!selectedNode) { // from the deepest node, traverse towards the root node // find the deepest node whose created rule is a left recursive rule and whose parent is not ParsingTreeObject* lrec = 0; ParsingTreeNode* current = found; while (current) { ParsingTreeObject* obj = dynamic_cast(current); if (obj) { FOREACH(WString, rule, obj->GetCreatorRules()) { if (leftRecursiveRules.Contains(rule)) { lrec = obj; break; } } if (obj && lrec && lrec != obj) { selectedNode = lrec; break; } } current = current->GetParent(); } } if (!selectedNode) { // if there is no left recursive rule that creates the deepest node and all indirect parents // choose the deepest ParsingTreeObject ParsingTreeNode* current = found; while (current) { ParsingTreeObject* obj = dynamic_cast(current); if (obj) { selectedNode = obj; break; } current = current->GetParent(); } } if (selectedNode) { // get the code range of the selected node start = selectedNode->GetCodeRange().start; end = selectedNode->GetCodeRange().end; // get all properties from the selected node newContext.rule = selectedNode->GetCreatorRules()[selectedNode->GetCreatorRules().Count() - 1]; newContext.originalRange = selectedNode->GetCodeRange(); newContext.originalNode = dynamic_cast(selectedNode); newContext.modifiedNode = newContext.originalNode; newContext.modifiedEditVersion = newContext.input.editVersion; // get the corresponding code of the selected node if (start.index >= 0 && end.index >= 0) { newContext.modifiedCode = newContext.input.code.Sub(start.index, end.index - start.index + 1).Buffer(); } } } bool GuiGrammarAutoComplete::NormalizeTextPos(AutoCompleteContext& newContext, elements::text::TextLines& lines, TextPos& pos) { // get the start position TextPos start(newContext.originalRange.start.row, newContext.originalRange.start.column); // get the end position of the end of lines TextPos end = lines.GetCount() <= 1 ? TextPos(start.row, start.column + lines.GetLine(0).dataLength) : TextPos(start.row + lines.GetCount() - 1, lines.GetLine(lines.GetCount() - 1).dataLength) ; if (start <= pos && pos <= end) { // if the pos is inside the range // normalize the pos to a new coordinate that the beginning position of lines is (row=0, column=0) pos.row -= start.row; if (pos.row == 0) { pos.column -= start.column; } return true; } else { return false; } } void GuiGrammarAutoComplete::ExecuteEdit(AutoCompleteContext& newContext) { // process the input of a task that is submitted by text editing // this function make an approximiation to the context if the RepeatingParsingExecutor is not fast enough // copy all TextEditNotifyStruct that is caused by a text editing before (and including) the edit version of the input List usedTrace; { SPIN_LOCK(editTraceLock) { CopyFrom( usedTrace, From(editTrace) .Where([&newContext](const TextEditNotifyStruct& value) { return (value.originalText != L"" || value.inputText != L"") && value.editVersion > newContext.modifiedEditVersion; }) ); } } // apply all modification to get the new modifiedCode bool failed = false; if (usedTrace.Count() > 0) { if (usedTrace[0].editVersion != newContext.modifiedEditVersion + 1) { // failed if any TextEditNotifyStruct is missing failed = true; } else { // initialize a TextLines with the latest modifiedCode text::TextLines lines(nullptr); lines.SetText(newContext.modifiedCode); FOREACH(TextEditNotifyStruct, trace, usedTrace) { // apply a modification to lines TextPos start = trace.originalStart; TextPos end = trace.originalEnd; // only if the modification is meaningful if (NormalizeTextPos(newContext, lines, start) && NormalizeTextPos(newContext, lines, end)) { lines.Modify(start, end, trace.inputText); } else { // otherwise, failed failed = true; break; } } if (!failed) { newContext.modifiedCode = lines.GetText(); } } } if (failed) { // clear originalNode to notify that the current context goes wrong newContext.originalNode = 0; } if (usedTrace.Count() > 0) { // update the edit version newContext.modifiedEditVersion = usedTrace[usedTrace.Count() - 1].editVersion; } } void GuiGrammarAutoComplete::DeleteFutures(collections::List& futures) { // delete all futures and clear the list FOREACH(ParsingState::Future*, future, futures) { delete future; } futures.Clear(); } regex::RegexToken* GuiGrammarAutoComplete::TraverseTransitions( parsing::tabling::ParsingState& state, parsing::tabling::ParsingTransitionCollector& transitionCollector, TextPos stopPosition, collections::List& nonRecoveryFutures, collections::List& recoveryFutures ) { const List& transitions = transitionCollector.GetTransitions(); for (vint index = 0; index < transitions.Count(); index++) { const ParsingState::TransitionResult& transition = transitions[index]; switch (transition.transitionType) { case ParsingState::TransitionResult::AmbiguityBegin: break; case ParsingState::TransitionResult::AmbiguityBranch: // ambiguity branches are not nested // tokens in different braches are the same // so we only need to run one branch, and skip the others index = transitionCollector.GetAmbiguityEndFromBegin(transitionCollector.GetAmbiguityBeginFromBranch(index)); break; case ParsingState::TransitionResult::AmbiguityEnd: break; case ParsingState::TransitionResult::ExecuteInstructions: { // test does the token reach the stop position if (transition.token) { // we treat "A|B" as editing A if token A is endless, otherwise treated as editing B TextPos tokenEnd(transition.token->rowEnd, transition.token->columnEnd + 1); // if the caret is not at the end of the token if (tokenEnd > stopPosition) { // stop the traversing and return the editing token return transition.token; } else if (tokenEnd == stopPosition) { // if the caret is at the end of the token, and it is a closed token // e.g. identifier is not a closed token, string is a closed token if (!grammarParser->GetTable()->GetLexer().Walk().IsClosedToken(transition.token->reading, transition.token->length)) { // stop the traversing and return the editing token return transition.token; } } } // traverse the PDA using the token specified in the current transition vint tableTokenIndex = transition.tableTokenIndex; List possibilities; if (recoveryFutures.Count() > 0) { FOREACH(ParsingState::Future*, future, recoveryFutures) { state.Explore(tableTokenIndex, future, possibilities); } } else { FOREACH(ParsingState::Future*, future, nonRecoveryFutures) { state.Explore(tableTokenIndex, future, possibilities); } } // delete duplicated futures List selectedPossibilities; for (vint i = 0; i < possibilities.Count(); i++) { ParsingState::Future* candidateFuture = possibilities[i]; bool duplicated = false; FOREACH(ParsingState::Future*, future, selectedPossibilities) { if ( candidateFuture->currentState == future->currentState && candidateFuture->reduceStateCount == future->reduceStateCount && candidateFuture->shiftStates.Count() == future->shiftStates.Count() ) { bool same = true; for (vint j = 0; j < future->shiftStates.Count(); j++) { if (candidateFuture->shiftStates[i] != future->shiftStates[i]) { same = false; break; } } if ((duplicated = same)) { break; } } } if (duplicated) { delete candidateFuture; } else { selectedPossibilities.Add(candidateFuture); } } // step forward if (transition.token || transition.tableTokenIndex == ParsingTable::TokenBegin) { DeleteFutures(nonRecoveryFutures); DeleteFutures(recoveryFutures); CopyFrom(nonRecoveryFutures, selectedPossibilities); } else { DeleteFutures(recoveryFutures); CopyFrom(recoveryFutures, selectedPossibilities); } } break; default:; } } return 0; } regex::RegexToken* GuiGrammarAutoComplete::SearchValidInputToken( parsing::tabling::ParsingState& state, parsing::tabling::ParsingTransitionCollector& transitionCollector, TextPos stopPosition, AutoCompleteContext& newContext, collections::SortedList& tableTokenIndices ) { // initialize the PDA state state.Reset(newContext.rule); List nonRecoveryFutures, recoveryFutures; nonRecoveryFutures.Add(state.ExploreCreateRootFuture()); // traverse the PDA until it reach the stop position // nonRecoveryFutures store the state when the last token (existing) is reached // recoveryFutures store the state when the last token (inserted by error recovery) is reached RegexToken* token = TraverseTransitions(state, transitionCollector, stopPosition, nonRecoveryFutures, recoveryFutures); // explore all possibilities from the last token before the stop position List possibilities; for (vint i = 0; i < nonRecoveryFutures.Count(); i++) { state.Explore(ParsingTable::NormalReduce, nonRecoveryFutures[i], nonRecoveryFutures); state.Explore(ParsingTable::LeftRecursiveReduce, nonRecoveryFutures[i], nonRecoveryFutures); } FOREACH(ParsingState::Future*, future, nonRecoveryFutures) { vint count = state.GetTable()->GetTokenCount(); for (vint i = ParsingTable::UserTokenStart; i < count; i++) { state.Explore(i, future, possibilities); } } // get all possible tokens that marked using @AutoCompleteCandidate FOREACH(ParsingState::Future*, future, possibilities) { if (!tableTokenIndices.Contains(future->selectedToken)) { tableTokenIndices.Add(future->selectedToken); } } // release all data DeleteFutures(possibilities); DeleteFutures(nonRecoveryFutures); DeleteFutures(recoveryFutures); // return the editing token return token; } TextPos GuiGrammarAutoComplete::GlobalTextPosToModifiedTextPos(AutoCompleteContext& newContext, TextPos pos) { pos.row-=newContext.originalRange.start.row; if(pos.row==0) { pos.column-=newContext.originalRange.start.column; } return pos; } TextPos GuiGrammarAutoComplete::ModifiedTextPosToGlobalTextPos(AutoCompleteContext& newContext, TextPos pos) { if(pos.row==0) { pos.column+=newContext.originalRange.start.column; } pos.row+=newContext.originalRange.start.row; return pos; } void GuiGrammarAutoComplete::ExecuteCalculateList(AutoCompleteContext& newContext) { // calcuate the content of the auto complete list // it is sad that, because the parser's algorithm is too complex // we need to reparse and track the internal state of the PDA(push-down automaton) here. // initialize the PDA ParsingState state(newContext.modifiedCode, grammarParser->GetTable()); state.Reset(newContext.rule); // prepare to get all transitions ParsingTransitionCollector collector; List> errors; // reparse and get all transitions during parsing if (grammarParser->Parse(state, collector, errors)) { // if modifiedNode is not prepared (the task is submitted because of text editing) // use the transition to build the syntax tree if (!newContext.modifiedNode) { ParsingTreeBuilder builder; builder.Reset(); bool succeeded = true; FOREACH(ParsingState::TransitionResult, transition, collector.GetTransitions()) { if (!(succeeded = builder.Run(transition))) { break; } } if (succeeded) { Ptr parsedNode = builder.GetNode(); newContext.modifiedNode = parsedNode.Cast(); newContext.modifiedNode->InitializeQueryCache(); } } if (newContext.modifiedNode) { // get the latest text editing trace TextEditNotifyStruct trace; SPIN_LOCK(editTraceLock) { vint index = UnsafeGetEditTraceIndex(newContext.modifiedEditVersion); if (index == -1) { return; } else { trace = editTrace[index]; } } // calculate the stop position for PDA traversing TextPos stopPosition = GlobalTextPosToModifiedTextPos(newContext, trace.inputStart); // find all possible token before the current caret using the PDA Ptr autoComplete = new AutoCompleteData; SortedList tableTokenIndices; RegexToken* editingToken = SearchValidInputToken(state, collector, stopPosition, newContext, tableTokenIndices); // collect all auto complete types { // collect all keywords that can be put into the auto complete list FOREACH(vint, token, tableTokenIndices) { vint regexToken = token - ParsingTable::UserTokenStart; if (regexToken >= 0) { autoComplete->candidates.Add(regexToken); if (parsingExecutor->GetTokenMetaData(regexToken).isCandidate) { autoComplete->shownCandidates.Add(regexToken); } } } // calculate the arranged stopPosition if (editingToken) { TextPos tokenPos(editingToken->rowStart, editingToken->columnStart); if (tokenPos < stopPosition) { stopPosition = tokenPos; } } // calculate the start/end position for PDA traversing TextPos startPos, endPos; { startPos = ModifiedTextPosToGlobalTextPos(newContext, stopPosition); autoComplete->startPosition = startPos; endPos = trace.inputEnd; if (newContext.modifiedNode != newContext.originalNode) { startPos = GlobalTextPosToModifiedTextPos(newContext, startPos); endPos = GlobalTextPosToModifiedTextPos(newContext, endPos); } if (startPos0) { endPos.column--; } } // calculate the auto complete type if (editingToken && parsingExecutor->GetTokenMetaData(editingToken->token).hasAutoComplete) { ParsingTextRange range(ParsingTextPos(startPos.row, startPos.column), ParsingTextPos(endPos.row, endPos.column)); AutoCompleteData::RetriveContext(*autoComplete.Obj(), range, newContext.modifiedNode.Obj(), parsingExecutor.Obj()); } } newContext.autoComplete = autoComplete; } } } void GuiGrammarAutoComplete::Execute(const RepeatingParsingOutput& input) { SPIN_LOCK(contextLock) { if(input.editVersionInvokeInMainThread(ownerComposition->GetRelatedControlHost(), [=]() { PostList(newContext, byGlobalCorrection); }); } } void GuiGrammarAutoComplete::PostList(const AutoCompleteContext& newContext, bool byGlobalCorrection) { bool openList = true; // true: make the list visible bool keepListState = false; // true: don't change the list visibility Ptr autoComplete = newContext.autoComplete; // if failed to get the auto complete list, close if (!autoComplete) { openList = false; } if (openList) { if (autoComplete->shownCandidates.Count() + autoComplete->candidateItems.Count() == 0) { openList = false; } } TextPos startPosition, endPosition; WString editingText; if (openList) { SPIN_LOCK(editTraceLock) { // if the edit version is invalid, cancel vint traceIndex = UnsafeGetEditTraceIndex(newContext.modifiedEditVersion); if (traceIndex == -1) { return; } // an edit version has two trace at most, for text change and caret change, here we peak the text change if (traceIndex > 0 && editTrace[traceIndex - 1].editVersion == context.modifiedEditVersion) { traceIndex--; } // if the edit version is not created by keyboard input, close if (traceIndex >= 0) { TextEditNotifyStruct& trace = editTrace[traceIndex]; if (!trace.keyInput) { openList = false; } } // scan all traces from the calculation's edit version until now if (openList) { keepListState = true; startPosition = autoComplete->startPosition; endPosition = editTrace[editTrace.Count() - 1].inputEnd; for (vint i = traceIndex; i < editTrace.Count(); i++) { TextEditNotifyStruct& trace = editTrace[i]; // if there are no text change trace until now, don't change the list if (trace.originalText != L"" || trace.inputText != L"") { keepListState = false; } // if the edit position goes before the start position of the auto complete, refresh if (trace.inputEnd <= startPosition) { openList = false; break; } } } if (traceIndex > 0) { editTrace.RemoveRange(0, traceIndex); } } } // if there is a global correction send to the UI thread but the list is not opening, cancel if (byGlobalCorrection && !IsListOpening()) { return; } // if the input text from the start position to the current position crosses a token, close if (openList && element) { editingText = element->GetLines().GetText(startPosition, endPosition); if (grammarParser->GetTable()->GetLexer().Walk().IsClosedToken(editingText)) { openList = false; } } // calculate the content of the list if (autoComplete && ((!keepListState && openList) || IsListOpening())) { SortedList itemKeys; List itemValues; // copy all candidate keywords FOREACH(vint, token, autoComplete->shownCandidates) { WString literal = parsingExecutor->GetTokenMetaData(token).unescapedRegexText; if (literal != L"" && !itemKeys.Contains(literal)) { ParsingCandidateItem item; item.name = literal; item.semanticId = -1; itemValues.Insert(itemKeys.Add(literal), item); } } // copy all candidate symbols if (autoComplete->acceptableSemanticIds) { FOREACH(ParsingCandidateItem, item, autoComplete->candidateItems) { if (autoComplete->acceptableSemanticIds->Contains(item.semanticId)) { // add all acceptable display of a symbol // because a symbol can has multiple representation in different places if (item.name != L"" && !itemKeys.Contains(item.name)) { itemValues.Insert(itemKeys.Add(item.name), item); } } } } // fill the list List candidateItems; for (vint i = 0; i < itemValues.Count(); i++) { auto& item = itemValues[i]; if (item.tag.IsNull()) { if (auto analyzer = parsingExecutor->GetAnalyzer()) { item.tag = analyzer->CreateTagForCandidateItem(item); } } GuiTextBoxAutoCompleteBase::AutoCompleteItem candidateItem; candidateItem.text = item.name; candidateItem.tag = item.tag; candidateItems.Add(candidateItem); } SetListContent(candidateItems); } // set the list state if (!keepListState) { if (openList) { OpenList(startPosition); } else { CloseList(); } } if (IsListOpening()) { HighlightList(editingText); } } void GuiGrammarAutoComplete::Initialize() { grammarParser=CreateAutoRecoverParser(parsingExecutor->GetParser()->GetTable()); CollectLeftRecursiveRules(); parsingExecutor->AttachCallback(this); } void GuiGrammarAutoComplete::OnContextFinishedAsync(AutoCompleteContext& context) { if (auto analyzer = parsingExecutor->GetAnalyzer()) { if (context.autoComplete && context.autoComplete->acceptableSemanticIds) { analyzer->GetCandidateItemsAsync(*context.autoComplete.Obj(), context, context.autoComplete->candidateItems); } } } void GuiGrammarAutoComplete::EnsureAutoCompleteFinished() { parsingExecutor->EnsureTaskFinished(); SPIN_LOCK(contextLock) { context = AutoCompleteContext(); } } GuiGrammarAutoComplete::GuiGrammarAutoComplete(Ptr _parsingExecutor) :RepeatingParsingExecutor::CallbackBase(_parsingExecutor) ,editing(false) { Initialize(); } GuiGrammarAutoComplete::GuiGrammarAutoComplete(Ptr _grammarParser, const WString& _grammarRule) :RepeatingParsingExecutor::CallbackBase(new RepeatingParsingExecutor(_grammarParser, _grammarRule)) ,editing(false) { Initialize(); } GuiGrammarAutoComplete::~GuiGrammarAutoComplete() { EnsureAutoCompleteFinished(); parsingExecutor->DetachCallback(this); } Ptr GuiGrammarAutoComplete::GetParsingExecutor() { return parsingExecutor; } } } } /*********************************************************************** .\CONTROLS\TEXTEDITORPACKAGE\LANGUAGESERVICE\GUILANGUAGEOPERATIONS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace collections; using namespace parsing; using namespace parsing::tabling; using namespace regex_internal; /*********************************************************************** ParsingContext ***********************************************************************/ bool ParsingTokenContext::RetriveContext(ParsingTokenContext& output, parsing::ParsingTreeNode* foundNode, RepeatingParsingExecutor* executor) { ParsingTreeToken* foundToken=dynamic_cast(foundNode); if(!foundToken) return false; ParsingTreeObject* tokenParent=dynamic_cast(foundNode->GetParent()); if(!tokenParent) return false; vint index=tokenParent->GetMembers().Values().IndexOf(foundNode); if(index==-1) return false; WString type=tokenParent->GetType(); WString field=tokenParent->GetMembers().Keys().Get(index); const RepeatingParsingExecutor::FieldMetaData& md=executor->GetFieldMetaData(type, field); output.foundToken=foundToken; output.tokenParent=tokenParent; output.type=type; output.field=field; output.acceptableSemanticIds=md.semantics; return true; } bool ParsingTokenContext::RetriveContext(ParsingTokenContext& output, parsing::ParsingTextPos pos, parsing::ParsingTreeObject* rootNode, RepeatingParsingExecutor* executor) { ParsingTreeNode* foundNode=rootNode->FindDeepestNode(pos); if(!foundNode) return false; return RetriveContext(output, foundNode, executor); } bool ParsingTokenContext::RetriveContext(ParsingTokenContext& output, parsing::ParsingTextRange range, ParsingTreeObject* rootNode, RepeatingParsingExecutor* executor) { ParsingTreeNode* foundNode=rootNode->FindDeepestNode(range); if(!foundNode) return false; return RetriveContext(output, foundNode, executor); } /*********************************************************************** RepeatingParsingExecutor::IParsingAnalyzer ***********************************************************************/ parsing::ParsingTreeNode* RepeatingParsingExecutor::IParsingAnalyzer::ToParent(parsing::ParsingTreeNode* node, const RepeatingPartialParsingOutput* output) { if (!output || !output->modifiedNode) return node; return node == output->modifiedNode.Obj() ? output->originalNode.Obj() : node; } parsing::ParsingTreeObject* RepeatingParsingExecutor::IParsingAnalyzer::ToChild(parsing::ParsingTreeObject* node, const RepeatingPartialParsingOutput* output) { if (!output || !output->modifiedNode) return node; return node == output->originalNode.Obj() ? output->modifiedNode.Obj() : node; } Ptr RepeatingParsingExecutor::IParsingAnalyzer::ToChild(Ptr node, const RepeatingPartialParsingOutput* output) { if (!output) return node; return node == output->originalNode ? output->modifiedNode.Cast() : node; } parsing::ParsingTreeNode* RepeatingParsingExecutor::IParsingAnalyzer::GetParent(parsing::ParsingTreeNode* node, const RepeatingPartialParsingOutput* output) { return ToParent(node, output)->GetParent(); } Ptr RepeatingParsingExecutor::IParsingAnalyzer::GetMember(parsing::ParsingTreeObject* node, const WString& name, const RepeatingPartialParsingOutput* output) { return ToChild(ToChild(node, output)->GetMember(name), output); } Ptr RepeatingParsingExecutor::IParsingAnalyzer::GetItem(parsing::ParsingTreeArray* node, vint index, const RepeatingPartialParsingOutput* output) { return ToChild(node->GetItem(index), output); } /*********************************************************************** RepeatingParsingExecutor::CallbackBase ***********************************************************************/ RepeatingParsingExecutor::CallbackBase::CallbackBase(Ptr _parsingExecutor) :parsingExecutor(_parsingExecutor) ,callbackAutoPushing(false) ,callbackElement(0) ,callbackElementModifyLock(0) { } RepeatingParsingExecutor::CallbackBase::~CallbackBase() { } void RepeatingParsingExecutor::CallbackBase::RequireAutoSubmitTask(bool enabled) { callbackAutoPushing=enabled; } void RepeatingParsingExecutor::CallbackBase::Attach(elements::GuiColorizedTextElement* _element, SpinLock& _elementModifyLock, compositions::GuiGraphicsComposition* _ownerComposition, vuint editVersion) { if(_element) { SPIN_LOCK(_elementModifyLock) { callbackElement=_element; callbackElementModifyLock=&_elementModifyLock; } } parsingExecutor->ActivateCallback(this); if(callbackElement && callbackElementModifyLock && callbackAutoPushing) { SPIN_LOCK(*callbackElementModifyLock) { RepeatingParsingInput input; input.editVersion=editVersion; input.code=callbackElement->GetLines().GetText(); parsingExecutor->SubmitTask(input); } } } void RepeatingParsingExecutor::CallbackBase::Detach() { if(callbackElement && callbackElementModifyLock) { SPIN_LOCK(*callbackElementModifyLock) { callbackElement=0; callbackElementModifyLock=0; } } parsingExecutor->DeactivateCallback(this); } void RepeatingParsingExecutor::CallbackBase::TextEditPreview(TextEditPreviewStruct& arguments) { } void RepeatingParsingExecutor::CallbackBase::TextEditNotify(const TextEditNotifyStruct& arguments) { } void RepeatingParsingExecutor::CallbackBase::TextCaretChanged(const TextCaretChangedStruct& arguments) { } void RepeatingParsingExecutor::CallbackBase::TextEditFinished(vuint editVersion) { if(callbackElement && callbackElementModifyLock && callbackAutoPushing) { SPIN_LOCK(*callbackElementModifyLock) { RepeatingParsingInput input; input.editVersion=editVersion; input.code=callbackElement->GetLines().GetText(); parsingExecutor->SubmitTask(input); } } } /*********************************************************************** RepeatingParsingExecutor ***********************************************************************/ void RepeatingParsingExecutor::Execute(const RepeatingParsingInput& input) { List> errors; Ptr node=grammarParser->Parse(input.code, grammarRule, errors).Cast(); if(node) { node->InitializeQueryCache(); } RepeatingParsingOutput result; result.node=node; result.editVersion=input.editVersion; result.code=input.code; if(node) { OnContextFinishedAsync(result); FOREACH(ICallback*, callback, callbacks) { callback->OnParsingFinishedAsync(result); } } } void RepeatingParsingExecutor::PrepareMetaData() { Ptr table=grammarParser->GetTable(); tokenIndexMap.Clear(); semanticIndexMap.Clear(); tokenMetaDatas.Clear(); fieldMetaDatas.Clear(); Dictionary> tokenColorAtts, tokenContextColorAtts, tokenCandidateAtts, tokenAutoCompleteAtts; Dictionary> fieldColorAtts, fieldSemanticAtts; { vint tokenCount=table->GetTokenCount(); for(vint token=ParsingTable::UserTokenStart;tokenGetTokenInfo(token); vint tokenIndex=token-ParsingTable::UserTokenStart; tokenIndexMap.Add(tokenInfo.name, tokenIndex); if(Ptr att=GetColorAttribute(tokenInfo.attributeIndex)) { tokenColorAtts.Add(tokenIndex, att); } if(Ptr att=GetContextColorAttribute(tokenInfo.attributeIndex)) { tokenContextColorAtts.Add(tokenIndex, att); } if(Ptr att=GetCandidateAttribute(tokenInfo.attributeIndex)) { tokenCandidateAtts.Add(tokenIndex, att); } if(Ptr att=GetAutoCompleteAttribute(tokenInfo.attributeIndex)) { tokenAutoCompleteAtts.Add(tokenIndex, att); } } } { vint fieldCount=table->GetTreeFieldInfoCount(); for(vint field=0;fieldGetTreeFieldInfo(field); FieldDesc fieldDesc(fieldInfo.type, fieldInfo.field); if(Ptr att=GetColorAttribute(fieldInfo.attributeIndex)) { fieldColorAtts.Add(fieldDesc, att); } if(Ptr att=GetSemanticAttribute(fieldInfo.attributeIndex)) { fieldSemanticAtts.Add(fieldDesc, att); } } } FOREACH(Ptr, att, From(tokenColorAtts.Values()) .Concat(tokenContextColorAtts.Values()) .Concat(fieldColorAtts.Values()) .Concat(fieldSemanticAtts.Values()) ) { FOREACH(WString, argument, att->arguments) { if(!semanticIndexMap.Contains(argument)) { semanticIndexMap.Add(argument); } } } vint index=0; FOREACH(vint, tokenIndex, tokenIndexMap.Values()) { TokenMetaData md; md.tableTokenIndex=tokenIndex+ParsingTable::UserTokenStart; md.lexerTokenIndex=tokenIndex; md.defaultColorIndex=-1; md.hasContextColor=false; md.hasAutoComplete=false; md.isCandidate=false; if((index=tokenColorAtts.Keys().IndexOf(tokenIndex))!=-1) { md.defaultColorIndex=semanticIndexMap.IndexOf(tokenColorAtts.Values()[index]->arguments[0]); } md.hasContextColor=tokenContextColorAtts.Keys().Contains(tokenIndex); md.hasAutoComplete=tokenAutoCompleteAtts.Keys().Contains(tokenIndex); if((md.isCandidate=tokenCandidateAtts.Keys().Contains(tokenIndex))) { const ParsingTable::TokenInfo& tokenInfo=table->GetTokenInfo(md.tableTokenIndex); if(IsRegexEscapedLiteralString(tokenInfo.regex)) { md.unescapedRegexText=UnescapeTextForRegex(tokenInfo.regex); } else { md.isCandidate=false; } } tokenMetaDatas.Add(tokenIndex, md); } { vint fieldCount=table->GetTreeFieldInfoCount(); for(vint field=0;fieldGetTreeFieldInfo(field); FieldDesc fieldDesc(fieldInfo.type, fieldInfo.field); FieldMetaData md; md.colorIndex=-1; if((index=fieldColorAtts.Keys().IndexOf(fieldDesc))!=-1) { md.colorIndex=semanticIndexMap.IndexOf(fieldColorAtts.Values()[index]->arguments[0]); } if((index=fieldSemanticAtts.Keys().IndexOf(fieldDesc))!=-1) { md.semantics=new List; FOREACH(WString, argument, fieldSemanticAtts.Values()[index]->arguments) { md.semantics->Add(semanticIndexMap.IndexOf(argument)); } } fieldMetaDatas.Add(fieldDesc, md); } } } void RepeatingParsingExecutor::OnContextFinishedAsync(RepeatingParsingOutput& context) { if(analyzer) { context.cache = analyzer->CreateCacheAsync(context); } } RepeatingParsingExecutor::RepeatingParsingExecutor(Ptr _grammarParser, const WString& _grammarRule, Ptr _analyzer) :grammarParser(_grammarParser) ,grammarRule(_grammarRule) ,analyzer(_analyzer) ,autoPushingCallback(0) { PrepareMetaData(); if (analyzer) { analyzer->Attach(this); } } RepeatingParsingExecutor::~RepeatingParsingExecutor() { EnsureTaskFinished(); if (analyzer) { analyzer->Detach(this); } } Ptr RepeatingParsingExecutor::GetParser() { return grammarParser; } bool RepeatingParsingExecutor::AttachCallback(ICallback* value) { if(!value) return false; if(callbacks.Contains(value)) return false; callbacks.Add(value); return true; } bool RepeatingParsingExecutor::DetachCallback(ICallback* value) { if(!value) return false; if(!callbacks.Contains(value)) return false; DeactivateCallback(value); callbacks.Remove(value); return true; } bool RepeatingParsingExecutor::ActivateCallback(ICallback* value) { if(!value) return false; if(!callbacks.Contains(value)) return false; if(activatedCallbacks.Contains(value)) return false; activatedCallbacks.Add(value); if(!autoPushingCallback) { autoPushingCallback=value; autoPushingCallback->RequireAutoSubmitTask(true); } return true; } bool RepeatingParsingExecutor::DeactivateCallback(ICallback* value) { if(!value) return false; if(!callbacks.Contains(value)) return false; if(!activatedCallbacks.Contains(value)) return false; if(autoPushingCallback==value) { autoPushingCallback->RequireAutoSubmitTask(false); autoPushingCallback=0; } activatedCallbacks.Remove(value); if(!autoPushingCallback && activatedCallbacks.Count()>0) { autoPushingCallback=activatedCallbacks[0]; autoPushingCallback->RequireAutoSubmitTask(true); } return true; } Ptr RepeatingParsingExecutor::GetAnalyzer() { return analyzer; } vint RepeatingParsingExecutor::GetTokenIndex(const WString& tokenName) { vint index=tokenIndexMap.Keys().IndexOf(tokenName); return index==-1?-1:tokenIndexMap.Values()[index]; } vint RepeatingParsingExecutor::GetSemanticId(const WString& name) { return semanticIndexMap.IndexOf(name); } WString RepeatingParsingExecutor::GetSemanticName(vint id) { return 0<=id&&id RepeatingParsingExecutor::GetAttribute(vint index, const WString& name, vint argumentCount) { if(index!=-1) { Ptr att=grammarParser->GetTable()->GetAttributeInfo(index)->FindFirst(name); if(att && (argumentCount==-1 || att->arguments.Count()==argumentCount)) { return att; } } return 0; } Ptr RepeatingParsingExecutor::GetColorAttribute(vint index) { return GetAttribute(index, L"Color", 1); } Ptr RepeatingParsingExecutor::GetContextColorAttribute(vint index) { return GetAttribute(index, L"ContextColor", 0); } Ptr RepeatingParsingExecutor::GetSemanticAttribute(vint index) { return GetAttribute(index, L"Semantic", -1); } Ptr RepeatingParsingExecutor::GetCandidateAttribute(vint index) { return GetAttribute(index, L"Candidate", 0); } Ptr RepeatingParsingExecutor::GetAutoCompleteAttribute(vint index) { return GetAttribute(index, L"AutoComplete", 0); } } } } /*********************************************************************** .\CONTROLS\TEXTEDITORPACKAGE\LANGUAGESERVICE\GUILANGUAGECOLORIZER.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace elements; using namespace parsing; using namespace parsing::tabling; using namespace collections; using namespace theme; /*********************************************************************** GuiGrammarColorizer ***********************************************************************/ void GuiGrammarColorizer::OnParsingFinishedAsync(const RepeatingParsingOutput& output) { SPIN_LOCK(contextLock) { context=output; OnContextFinishedAsync(context); } RestartColorizer(); } void GuiGrammarColorizer::OnContextFinishedAsync(const RepeatingParsingOutput& context) { } void GuiGrammarColorizer::Attach(elements::GuiColorizedTextElement* _element, SpinLock& _elementModifyLock, compositions::GuiGraphicsComposition* _ownerComposition, vuint editVersion) { GuiTextBoxRegexColorizer::Attach(_element, _elementModifyLock, _ownerComposition, editVersion); RepeatingParsingExecutor::CallbackBase::Attach(_element, _elementModifyLock, _ownerComposition, editVersion); } void GuiGrammarColorizer::Detach() { GuiTextBoxRegexColorizer::Detach(); RepeatingParsingExecutor::CallbackBase::Detach(); if(element && elementModifyLock) { parsingExecutor->EnsureTaskFinished(); StopColorizer(false); } } void GuiGrammarColorizer::TextEditPreview(TextEditPreviewStruct& arguments) { GuiTextBoxRegexColorizer::TextEditPreview(arguments); RepeatingParsingExecutor::CallbackBase::TextEditPreview(arguments); } void GuiGrammarColorizer::TextEditNotify(const TextEditNotifyStruct& arguments) { GuiTextBoxRegexColorizer::TextEditNotify(arguments); RepeatingParsingExecutor::CallbackBase::TextEditNotify(arguments); } void GuiGrammarColorizer::TextCaretChanged(const TextCaretChangedStruct& arguments) { GuiTextBoxRegexColorizer::TextCaretChanged(arguments); RepeatingParsingExecutor::CallbackBase::TextCaretChanged(arguments); } void GuiGrammarColorizer::TextEditFinished(vuint editVersion) { GuiTextBoxRegexColorizer::TextEditFinished(editVersion); RepeatingParsingExecutor::CallbackBase::TextEditFinished(editVersion); } void GuiGrammarColorizer::OnSemanticColorize(SemanticColorizeContext& context, const RepeatingParsingOutput& input) { if (auto analyzer = parsingExecutor->GetAnalyzer()) { auto semanticId = analyzer->GetSemanticIdForTokenAsync(context, input); if(semanticId!=-1) { context.semanticId=semanticId; } } } void GuiGrammarColorizer::EnsureColorizerFinished() { parsingExecutor->EnsureTaskFinished(); StopColorizerForever(); SPIN_LOCK(contextLock) { context=RepeatingParsingOutput(); } } GuiGrammarColorizer::GuiGrammarColorizer(Ptr _parsingExecutor) :RepeatingParsingExecutor::CallbackBase(_parsingExecutor) { parsingExecutor->AttachCallback(this); BeginSetColors(); } GuiGrammarColorizer::GuiGrammarColorizer(Ptr _grammarParser, const WString& _grammarRule) :RepeatingParsingExecutor::CallbackBase(new RepeatingParsingExecutor(_grammarParser, _grammarRule)) { parsingExecutor->AttachCallback(this); BeginSetColors(); } GuiGrammarColorizer::~GuiGrammarColorizer() { EnsureColorizerFinished(); parsingExecutor->DetachCallback(this); } void GuiGrammarColorizer::BeginSetColors() { ClearTokens(); colorSettings.Clear(); text::ColorEntry entry; { entry.normal.text = Color(0, 0, 0); entry.normal.background = Color(0, 0, 0, 0); entry.selectedFocused.text = Color(255, 255, 255); entry.selectedFocused.background = Color(51, 153, 255); entry.selectedUnfocused.text = Color(255, 255, 255); entry.selectedUnfocused.background = Color(51, 153, 255); } SetDefaultColor(entry); colorSettings.Add(L"Default", entry); } const collections::SortedList& GuiGrammarColorizer::GetColorNames() { return colorSettings.Keys(); } GuiGrammarColorizer::ColorEntry GuiGrammarColorizer::GetColor(const WString& name) { vint index=colorSettings.Keys().IndexOf(name); return index==-1?GetDefaultColor():colorSettings.Values().Get(index); } void GuiGrammarColorizer::SetColor(const WString& name, const ColorEntry& entry) { colorSettings.Set(name, entry); } void GuiGrammarColorizer::SetColor(const WString& name, const Color& color) { text::ColorEntry entry=GetDefaultColor(); entry.normal.text=color; SetColor(name, entry); } void GuiGrammarColorizer::EndSetColors() { SortedList tokenColors; Ptr table=parsingExecutor->GetParser()->GetTable(); semanticColorMap.Clear(); vint tokenCount=table->GetTokenCount(); for(vint token=ParsingTable::UserTokenStart;tokenGetTokenInfo(token); const RepeatingParsingExecutor::TokenMetaData& md=parsingExecutor->GetTokenMetaData(token-ParsingTable::UserTokenStart); if(md.defaultColorIndex==-1) { AddToken(tokenInfo.regex, GetDefaultColor()); } else { WString name=parsingExecutor->GetSemanticName(md.defaultColorIndex); vint color=AddToken(tokenInfo.regex, GetColor(name)); semanticColorMap.Set(md.defaultColorIndex, color); tokenColors.Add(name); } } FOREACH_INDEXER(WString, color, index, colorSettings.Keys()) { if(!tokenColors.Contains(color)) { vint semanticId=parsingExecutor->GetSemanticId(color); if(semanticId!=-1) { vint tokenId=AddExtraToken(colorSettings.Values().Get(index)); vint color=tokenId+tokenCount-ParsingTable::UserTokenStart; semanticColorMap.Set(semanticId, color); } } } Setup(); } void GuiGrammarColorizer::ColorizeTokenContextSensitive(vint lineIndex, const wchar_t* text, vint start, vint length, vint& token, vint& contextState) { SPIN_LOCK(contextLock) { ParsingTreeObject* node=context.node.Obj(); if(node && token!=-1 && parsingExecutor->GetTokenMetaData(token).hasContextColor) { ParsingTextPos pos(lineIndex, start); SemanticColorizeContext scContext; if(SemanticColorizeContext::RetriveContext(scContext, pos, node, parsingExecutor.Obj())) { const RepeatingParsingExecutor::FieldMetaData& md=parsingExecutor->GetFieldMetaData(scContext.type, scContext.field); vint semantic=md.colorIndex; scContext.semanticId=-1; if(scContext.acceptableSemanticIds) { OnSemanticColorize(scContext, context); if(md.semantics->Contains(scContext.semanticId)) { semantic=scContext.semanticId; } } if(semantic!=-1) { vint index=semanticColorMap.Keys().IndexOf(semantic); if(index!=-1) { token=semanticColorMap.Values()[index]; } } } } } } Ptr GuiGrammarColorizer::GetParsingExecutor() { return parsingExecutor; } } } } /*********************************************************************** .\CONTROLS\TOOLSTRIPPACKAGE\GUIRIBBONCONTROLS.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace reflection::description; using namespace collections; using namespace compositions; using namespace theme; using namespace templates; /*********************************************************************** GuiRibbonTab ***********************************************************************/ void GuiRibbonTab::BeforeControlTemplateUninstalled_() { auto ct = GetControlTemplateObject(false); if (!ct) return; if (auto bhc = ct->GetBeforeHeadersContainer()) { bhc->RemoveChild(beforeHeaders); } if (auto ahc = ct->GetAfterHeadersContainer()) { ahc->RemoveChild(afterHeaders); } } void GuiRibbonTab::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); if (auto bhc = ct->GetBeforeHeadersContainer()) { bhc->AddChild(beforeHeaders); } if (auto ahc = ct->GetAfterHeadersContainer()) { ahc->AddChild(afterHeaders); } } GuiRibbonTab::GuiRibbonTab(theme::ThemeName themeName) :GuiTab(themeName) { beforeHeaders = new GuiBoundsComposition(); beforeHeaders->SetAlignmentToParent(Margin(0, 0, 0, 0)); beforeHeaders->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); afterHeaders = new GuiBoundsComposition(); afterHeaders->SetAlignmentToParent(Margin(0, 0, 0, 0)); afterHeaders->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); } GuiRibbonTab::~GuiRibbonTab() { if (!beforeHeaders->GetParent()) { SafeDeleteComposition(beforeHeaders); } if (!afterHeaders->GetParent()) { SafeDeleteComposition(afterHeaders); } } compositions::GuiGraphicsComposition* GuiRibbonTab::GetBeforeHeaders() { return beforeHeaders; } compositions::GuiGraphicsComposition* GuiRibbonTab::GetAfterHeaders() { return afterHeaders; } /*********************************************************************** GuiRibbonGroupCollection ***********************************************************************/ bool GuiRibbonGroupCollection::QueryInsert(vint index, GuiRibbonGroup* const& value) { return !value->GetBoundsComposition()->GetParent(); } void GuiRibbonGroupCollection::AfterInsert(vint index, GuiRibbonGroup* const& value) { value->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); auto item = new GuiStackItemComposition(); item->AddChild(value->GetBoundsComposition()); tabPage->stack->InsertStackItem(index, item); } void GuiRibbonGroupCollection::AfterRemove(vint index, vint count) { for (vint i = 0; i < count; i++) { auto item = tabPage->stack->GetStackItems()[index]; tabPage->stack->RemoveChild(item); item->RemoveChild(item->Children()[0]); delete item; } } GuiRibbonGroupCollection::GuiRibbonGroupCollection(GuiRibbonTabPage* _tabPage) :tabPage(_tabPage) { } GuiRibbonGroupCollection::~GuiRibbonGroupCollection() { } /*********************************************************************** GuiRibbonTabPage ***********************************************************************/ GuiRibbonTabPage::GuiRibbonTabPage(theme::ThemeName themeName) :GuiTabPage(themeName) , groups(this) { stack = new GuiStackComposition(); stack->SetDirection(GuiStackComposition::Horizontal); stack->SetAlignmentToParent(Margin(2, 2, 2, 2)); stack->SetPadding(2); stack->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); responsiveStack = new GuiResponsiveStackComposition(); responsiveStack->SetDirection(ResponsiveDirection::Horizontal); responsiveStack->SetAlignmentToParent(Margin(0, 0, 0, 0)); responsiveStack->AddChild(stack); responsiveContainer = new GuiResponsiveContainerComposition(); responsiveContainer->SetAlignmentToParent(Margin(0, 0, 0, 0)); responsiveContainer->SetResponsiveTarget(responsiveStack); containerComposition->AddChild(responsiveContainer); HighlightedChanged.SetAssociatedComposition(boundsComposition); } GuiRibbonTabPage::~GuiRibbonTabPage() { } bool GuiRibbonTabPage::GetHighlighted() { return highlighted; } void GuiRibbonTabPage::SetHighlighted(bool value) { if (highlighted != value) { highlighted = value; HighlightedChanged.Execute(GetNotifyEventArguments()); } } collections::ObservableListBase& GuiRibbonTabPage::GetGroups() { return groups; } /*********************************************************************** GuiRibbonGroupItemCollection ***********************************************************************/ bool GuiRibbonGroupItemCollection::QueryInsert(vint index, GuiControl* const& value) { return !value->GetBoundsComposition()->GetParent(); } void GuiRibbonGroupItemCollection::AfterInsert(vint index, GuiControl* const& value) { value->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); auto item = new GuiStackItemComposition(); item->AddChild(value->GetBoundsComposition()); group->stack->InsertStackItem(index, item); } void GuiRibbonGroupItemCollection::AfterRemove(vint index, vint count) { for (vint i = 0; i < count; i++) { auto item = group->stack->GetStackItems()[index]; group->stack->RemoveChild(item); item->RemoveChild(item->Children()[0]); delete item; } } GuiRibbonGroupItemCollection::GuiRibbonGroupItemCollection(GuiRibbonGroup* _group) :group(_group) { } GuiRibbonGroupItemCollection::~GuiRibbonGroupItemCollection() { } /*********************************************************************** GuiRibbonGroup::CommandExecutor ***********************************************************************/ GuiRibbonGroup::CommandExecutor::CommandExecutor(GuiRibbonGroup* _group) :group(_group) { } GuiRibbonGroup::CommandExecutor::~CommandExecutor() { } void GuiRibbonGroup::CommandExecutor::NotifyExpandButtonClicked() { group->ExpandButtonClicked.Execute(group->GetNotifyEventArguments()); } /*********************************************************************** GuiRibbonGroupMenu ***********************************************************************/ class GuiRibbonGroupMenu : public GuiMenu, public Description { private: IGuiMenuService::Direction GetPreferredDirection()override { return IGuiMenuService::Horizontal; } bool IsActiveState()override { return false; } public: GuiRibbonGroupMenu(theme::ThemeName themeName, GuiControl* _owner) :GuiMenu(themeName, _owner) { } }; /*********************************************************************** GuiRibbonGroup ***********************************************************************/ void GuiRibbonGroup::BeforeControlTemplateUninstalled_() { auto ct = GetControlTemplateObject(false); if (!ct) return; ct->SetCommands(nullptr); } void GuiRibbonGroup::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); ct->SetExpandable(expandable); ct->SetCollapsed(responsiveView->GetCurrentView() == responsiveFixedButton); ct->SetCommands(commandExecutor.Obj()); dropdownButton->SetControlTemplate(ct->GetLargeDropdownButtonTemplate()); dropdownMenu->SetControlTemplate(ct->GetSubMenuTemplate()); } void GuiRibbonGroup::OnBoundsChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { dropdownMenu->GetBoundsComposition()->SetPreferredMinSize(Size(0, containerComposition->GetBounds().Height())); } void GuiRibbonGroup::OnTextChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { dropdownButton->SetText(GetText()); } void GuiRibbonGroup::OnBeforeSubMenuOpening(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if (responsiveView->GetViews().Contains(responsiveFixedButton)) { auto currentDropdown = dropdownMenu; if (items.Count() == 1) { if (auto provider = items[0]->QueryTypedService()) { if (auto menu = provider->ProvideDropdownMenu()) { currentDropdown = menu; } } } dropdownButton->SetSubMenu(currentDropdown, false); } } void GuiRibbonGroup::OnBeforeSwitchingView(compositions::GuiGraphicsComposition* sender, compositions::GuiItemEventArgs& arguments) { if (auto ct = GetControlTemplateObject(false)) { ct->SetCollapsed(arguments.itemIndex == 1); } if (arguments.itemIndex == 0) { while (responsiveStack->LevelDown()); dropdownMenu->GetContainerComposition()->RemoveChild(stack); responsiveStack->AddChild(stack); dropdownButton->SetSubMenu(nullptr, false); } else { while (responsiveStack->LevelUp()); responsiveStack->RemoveChild(stack); dropdownMenu->GetContainerComposition()->AddChild(stack); } } GuiRibbonGroup::GuiRibbonGroup(theme::ThemeName themeName) :GuiControl(themeName) , items(this) { commandExecutor = new CommandExecutor(this); { stack = new GuiStackComposition(); stack->SetDirection(GuiStackComposition::Horizontal); stack->SetAlignmentToParent(Margin(0, 0, 0, 0)); stack->SetPadding(2); stack->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); responsiveStack = new GuiResponsiveStackComposition(); responsiveStack->SetDirection(ResponsiveDirection::Horizontal); responsiveStack->SetAlignmentToParent(Margin(0, 0, 0, 0)); responsiveStack->AddChild(stack); } { dropdownButton = new GuiToolstripButton(theme::ThemeName::RibbonLargeDropdownButton); dropdownButton->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); responsiveFixedButton = new GuiResponsiveFixedComposition(); responsiveFixedButton->SetAlignmentToParent(Margin(0, 0, 0, 0)); responsiveFixedButton->AddChild(dropdownButton->GetBoundsComposition()); dropdownMenu = new GuiRibbonGroupMenu(theme::ThemeName::Menu, dropdownButton); } responsiveView = new GuiResponsiveViewComposition(); responsiveView->SetAlignmentToParent(Margin(0, 0, 0, 0)); responsiveView->GetViews().Add(responsiveStack); containerComposition->AddChild(responsiveView); ExpandableChanged.SetAssociatedComposition(boundsComposition); ExpandButtonClicked.SetAssociatedComposition(boundsComposition); LargeImageChanged.SetAssociatedComposition(boundsComposition); TextChanged.AttachMethod(this, &GuiRibbonGroup::OnTextChanged); boundsComposition->BoundsChanged.AttachMethod(this, &GuiRibbonGroup::OnBoundsChanged); responsiveView->BeforeSwitchingView.AttachMethod(this, &GuiRibbonGroup::OnBeforeSwitchingView); dropdownButton->BeforeSubMenuOpening.AttachMethod(this, &GuiRibbonGroup::OnBeforeSubMenuOpening); } GuiRibbonGroup::~GuiRibbonGroup() { if (!responsiveView->GetViews().Contains(responsiveFixedButton)) { SafeDeleteComposition(responsiveFixedButton); } delete dropdownMenu; } bool GuiRibbonGroup::GetExpandable() { return expandable; } void GuiRibbonGroup::SetExpandable(bool value) { if (expandable != value) { expandable = value; GetControlTemplateObject(true)->SetExpandable(expandable); ExpandableChanged.Execute(GetNotifyEventArguments()); } } Ptr GuiRibbonGroup::GetLargeImage() { return largeImage; } void GuiRibbonGroup::SetLargeImage(Ptr value) { if (largeImage != value) { largeImage = value; dropdownButton->SetLargeImage(value); LargeImageChanged.Execute(GetNotifyEventArguments()); if (value) { if (!responsiveView->GetViews().Contains(responsiveFixedButton)) { responsiveView->GetViews().Add(responsiveFixedButton); } } else { if (responsiveView->GetViews().Contains(responsiveFixedButton)) { responsiveView->GetViews().Remove(responsiveFixedButton); } } } } collections::ObservableListBase& GuiRibbonGroup::GetItems() { return items; } /*********************************************************************** GuiRibbonIconLabel ***********************************************************************/ void GuiRibbonIconLabel::BeforeControlTemplateUninstalled_() { } void GuiRibbonIconLabel::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); ct->SetImage(image); } GuiRibbonIconLabel::GuiRibbonIconLabel(theme::ThemeName themeName) :GuiControl(themeName) { ImageChanged.SetAssociatedComposition(boundsComposition); } GuiRibbonIconLabel::~GuiRibbonIconLabel() { } Ptr GuiRibbonIconLabel::GetImage() { return image; } void GuiRibbonIconLabel::SetImage(Ptr value) { if (image != value) { image = value; GetControlTemplateObject(true)->SetImage(image); ImageChanged.Execute(GetNotifyEventArguments()); } } /*********************************************************************** GuiRibbonButtonsItemCollection ***********************************************************************/ bool GuiRibbonButtonsItemCollection::QueryInsert(vint index, GuiControl* const& value) { return !value->GetBoundsComposition()->GetParent(); } void GuiRibbonButtonsItemCollection::AfterInsert(vint index, GuiControl* const& value) { buttons->responsiveView->GetSharedControls().Add(value); buttons->SetButtonThemeName(buttons->responsiveView->GetCurrentView(), value); for (vint i = 0; i < sizeof(buttons->views) / sizeof(*buttons->views); i++) { if (auto view = buttons->views[i]) { auto stack = dynamic_cast(view->Children()[0]); auto shared = new GuiResponsiveSharedComposition(); shared->SetAlignmentToParent(Margin(0, 0, 0, 0)); shared->SetShared(value); auto item = new GuiStackItemComposition(); item->AddChild(shared); stack->InsertStackItem(index, item); } } } void GuiRibbonButtonsItemCollection::BeforeRemove(vint index, GuiControl* const& value) { CHECK_FAIL(L"GuiRibbonButtonsItemCollection::BeforeRemove(vint, GuiControl* const&)#Controls are not allowed to be removed from GuiRibbonButtons."); } GuiRibbonButtonsItemCollection::GuiRibbonButtonsItemCollection(GuiRibbonButtons* _buttons) :buttons(_buttons) { } GuiRibbonButtonsItemCollection::~GuiRibbonButtonsItemCollection() { } /*********************************************************************** GuiRibbonButtons ***********************************************************************/ void GuiRibbonButtons::BeforeControlTemplateUninstalled_() { } void GuiRibbonButtons::AfterControlTemplateInstalled_(bool initialize) { FOREACH(GuiControl*, button, buttons) { SetButtonThemeName(responsiveView->GetCurrentView(), button); } } void GuiRibbonButtons::OnBeforeSwitchingView(compositions::GuiGraphicsComposition* sender, compositions::GuiItemEventArgs& arguments) { FOREACH(GuiControl*, button, buttons) { SetButtonThemeName(responsiveView->GetViews()[arguments.itemIndex], button); } } void GuiRibbonButtons::SetButtonThemeName(compositions::GuiResponsiveCompositionBase* fixed, GuiControl* button) { if (fixed && button) { auto themeName = button->GetControlThemeName(); vint type = -1; switch (themeName) { case ThemeName::RibbonLargeButton: case ThemeName::RibbonSmallButton: case ThemeName::ToolstripButton: type = 0; break; case ThemeName::RibbonLargeDropdownButton: case ThemeName::RibbonSmallDropdownButton: case ThemeName::ToolstripDropdownButton: type = 1; break; case ThemeName::RibbonLargeSplitButton: case ThemeName::RibbonSmallSplitButton: case ThemeName::ToolstripSplitButton: type = 2; break; case ThemeName::RibbonSmallIconLabel: case ThemeName::RibbonIconLabel: type = 3; break; } if (type != -1) { ThemeName themeName = ThemeName::Unknown; TemplateProperty controlTemplate; if (fixed == views[(vint)RibbonButtonSize::Large]) { switch (type) { case 0: themeName = ThemeName::RibbonLargeButton; break; case 1: themeName = ThemeName::RibbonLargeDropdownButton; break; case 2: themeName = ThemeName::RibbonLargeSplitButton; break; case 3: themeName = ThemeName::RibbonSmallIconLabel; break; } } else if (fixed == views[(vint)RibbonButtonSize::Small]) { switch (type) { case 0: themeName = ThemeName::RibbonSmallButton; break; case 1: themeName = ThemeName::RibbonSmallDropdownButton; break; case 2: themeName = ThemeName::RibbonSmallSplitButton; break; case 3: themeName = ThemeName::RibbonSmallIconLabel; break; } } else if (fixed == views[(vint)RibbonButtonSize::Icon]) { switch (type) { case 0: themeName = ThemeName::ToolstripButton; break; case 1: themeName = ThemeName::ToolstripDropdownButton; break; case 2: themeName = ThemeName::ToolstripSplitButton; break; case 3: themeName = ThemeName::RibbonIconLabel; break; } } if (auto ct = GetControlTemplateObject(false)) { if (fixed == views[(vint)RibbonButtonSize::Large]) { switch (type) { case 0: controlTemplate = ct->GetLargeButtonTemplate(); break; case 1: controlTemplate = ct->GetLargeDropdownButtonTemplate(); break; case 2: controlTemplate = ct->GetLargeSplitButtonTemplate(); break; case 3: controlTemplate = ct->GetSmallIconLabelTemplate(); break; } } else if (fixed == views[(vint)RibbonButtonSize::Small]) { switch (type) { case 0: controlTemplate = ct->GetSmallButtonTemplate(); break; case 1: controlTemplate = ct->GetSmallDropdownButtonTemplate(); break; case 2: controlTemplate = ct->GetSmallSplitButtonTemplate(); break; case 3: controlTemplate = ct->GetSmallIconLabelTemplate(); break; } } else if (fixed == views[(vint)RibbonButtonSize::Icon]) { switch (type) { case 0: controlTemplate = ct->GetIconButtonTemplate(); break; case 1: controlTemplate = ct->GetIconDropdownButtonTemplate(); break; case 2: controlTemplate = ct->GetIconSplitButtonTemplate(); break; case 3: controlTemplate = ct->GetIconLabelTemplate(); break; } } } button->SetControlThemeNameAndTemplate(themeName, controlTemplate); } } } GuiRibbonButtons::GuiRibbonButtons(theme::ThemeName themeName, RibbonButtonSize _maxSize, RibbonButtonSize _minSize) :GuiControl(themeName) , maxSize(_maxSize) , minSize(_minSize) , buttons(this) { responsiveView = new GuiResponsiveViewComposition(); responsiveView->SetDirection(ResponsiveDirection::Horizontal); responsiveView->SetAlignmentToParent(Margin(0, 0, 0, 0)); responsiveView->BeforeSwitchingView.AttachMethod(this, &GuiRibbonButtons::OnBeforeSwitchingView); auto installButton = [&](GuiTableComposition* table, vint row, vint column, GuiControl* buttonContainer) { auto shared = new GuiResponsiveSharedComposition(); shared->SetAlignmentToParent(Margin(0, 0, 0, 0)); shared->SetShared(buttonContainer); auto cell = new GuiCellComposition(); cell->SetSite(row, column, 1, 1); cell->AddChild(shared); table->AddChild(cell); }; for (vint i = 0; i < sizeof(views) / sizeof(*views); i++) { if ((vint)maxSize <= i && i <= (vint)minSize) { auto stack = new GuiStackComposition(); stack->SetAlignmentToParent(Margin(0, 0, 0, 0)); stack->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); stack->SetDirection(i == 0 ? GuiStackComposition::Horizontal : GuiStackComposition::Vertical); views[i] = new GuiResponsiveFixedComposition(); views[i]->AddChild(stack); responsiveView->GetViews().Add(views[i]); } } auto sharedSizeRootComposition = new GuiSharedSizeRootComposition(); sharedSizeRootComposition->SetAlignmentToParent(Margin(0, 0, 0, 0)); sharedSizeRootComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); sharedSizeRootComposition->AddChild(responsiveView); containerComposition->AddChild(sharedSizeRootComposition); } GuiRibbonButtons::~GuiRibbonButtons() { } collections::ObservableListBase& GuiRibbonButtons::GetButtons() { return buttons; } /*********************************************************************** GuiRibbonToolstripsGroupCollection ***********************************************************************/ bool GuiRibbonToolstripsGroupCollection::QueryInsert(vint index, GuiToolstripGroup* const& value) { return !value->GetBoundsComposition()->GetParent(); } void GuiRibbonToolstripsGroupCollection::AfterInsert(vint index, GuiToolstripGroup* const& value) { toolstrips->RearrangeToolstripGroups(); } void GuiRibbonToolstripsGroupCollection::AfterRemove(vint index, vint count) { toolstrips->RearrangeToolstripGroups(); } GuiRibbonToolstripsGroupCollection::GuiRibbonToolstripsGroupCollection(GuiRibbonToolstrips* _toolstrips) :toolstrips(_toolstrips) { } GuiRibbonToolstripsGroupCollection::~GuiRibbonToolstripsGroupCollection() { } /*********************************************************************** GuiRibbonToolstrips ***********************************************************************/ #define ARRLEN(X) sizeof(X) / sizeof(*X) void GuiRibbonToolstrips::BeforeControlTemplateUninstalled_() { } void GuiRibbonToolstrips::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); for (vint i = 0; i < ARRLEN(toolbars); i++) { toolbars[i]->SetControlTemplate(ct->GetToolbarTemplate()); } } void GuiRibbonToolstrips::OnBeforeSwitchingView(compositions::GuiGraphicsComposition* sender, compositions::GuiItemEventArgs& arguments) { RearrangeToolstripGroups(arguments.itemIndex); } void GuiRibbonToolstrips::RearrangeToolstripGroups(vint viewIndex) { static_assert(ARRLEN(longContainers) == 2, ""); static_assert(ARRLEN(shortContainers) == 3, ""); if (viewIndex == -1) { viewIndex = responsiveView->GetViews().IndexOf(responsiveView->GetCurrentView()); } for (vint i = 0; i < ARRLEN(longContainers); i++) { longContainers[i]->GetToolstripItems().Clear(); } for (vint i = 0; i < ARRLEN(shortContainers); i++) { shortContainers[i]->GetToolstripItems().Clear(); } vint count = viewIndex == 0 ? 2 : 3; if (groups.Count() <= count) { auto containers = viewIndex == 0 ? longContainers : shortContainers; for (vint i = 0; i < groups.Count(); i++) { containers[i]->GetToolstripItems().Add(groups[i]); } } else if (count == 3) { #define DELTA(POSTFIX) (abs(count1##POSTFIX - count2##POSTFIX) + abs(count2##POSTFIX - count3##POSTFIX) + abs(count3##POSTFIX - count1##POSTFIX)) #define DEFINE_COUNT(POSTFIX, OFFSET_FIRST, OFFSET_LAST) \ vint count1##POSTFIX = count1_o + (OFFSET_FIRST); \ vint count2##POSTFIX = count2_o - (OFFSET_FIRST) - (OFFSET_LAST); \ vint count3##POSTFIX = count3_o + (OFFSET_LAST) #define MIN(a, b) (a)<(b)?(a):(b) vint firstGroupCount = 0; vint lastGroupCount = 0; vint count1_o = 0; vint count2_o = From(groups) .Select([](GuiToolstripGroup* group) {return group->GetToolstripItems().Count(); }) .Aggregate([](vint a, vint b) {return a + b; }); vint count3_o = 0; vint delta_o = DELTA(_o); while (firstGroupCount + lastGroupCount < groups.Count()) { auto newFirstGroup = groups[firstGroupCount]; auto newLastGroup = groups[groups.Count() - lastGroupCount - 1]; DEFINE_COUNT(_f, newFirstGroup->GetToolstripItems().Count(), 0); vint delta_f = DELTA(_f); DEFINE_COUNT(_l, 0, newLastGroup->GetToolstripItems().Count()); vint delta_l = DELTA(_l); vint delta = MIN(delta_o, MIN(delta_f, delta_l)); if (delta == delta_f) { firstGroupCount++; count1_o = count1_f; count2_o = count2_f; count3_o = count3_f; delta_o = delta_f; } else if (delta == delta_l) { lastGroupCount++; count1_o = count1_l; count2_o = count2_l; count3_o = count3_l; delta_o = delta_l; } else { break; } } vint minMiddle = firstGroupCount; vint maxMiddle = groups.Count() - lastGroupCount - 1; for (vint j = 0; j < groups.Count(); j++) { shortContainers[ j < minMiddle ? 0 : j>maxMiddle ? 2 : 1 ]->GetToolstripItems().Add(groups[j]); } #undef MIN #undef DEFINE_COUNT #undef DELTA } else if (count == 2) { vint firstGroupCount = groups.Count(); { vint count1 = 0; vint count2 = From(groups) .Select([](GuiToolstripGroup* group) {return group->GetToolstripItems().Count(); }) .Aggregate([](vint a, vint b) {return a + b; }); vint delta = abs(count2 - count1); for (vint i = 0; i < groups.Count(); i++) { auto groupCount = groups[i]->GetToolstripItems().Count(); vint count1_2 = count1 + groupCount; vint count2_2 = count2 - groupCount; vint delta_2 = abs(count2_2 - count1_2); if (delta < delta_2) { firstGroupCount = i; break; } count1 = count1_2; count2 = count2_2; delta = delta_2; } } for (vint j = 0; j < groups.Count(); j++) { longContainers[j < firstGroupCount ? 0 : 1]->GetToolstripItems().Add(groups[j]); } } } GuiRibbonToolstrips::GuiRibbonToolstrips(theme::ThemeName themeName) :GuiControl(themeName) , groups(this) { responsiveView = new GuiResponsiveViewComposition(); responsiveView->SetDirection(ResponsiveDirection::Horizontal); responsiveView->SetAlignmentToParent(Margin(0, 0, 0, 0)); responsiveView->BeforeSwitchingView.AttachMethod(this, &GuiRibbonToolstrips::OnBeforeSwitchingView); vint toolbarIndex = 0; for (vint i = 0; i < sizeof(views) / sizeof(*views); i++) { auto containers = i == 0 ? longContainers : shortContainers; vint count = i == 0 ? 2 : 3; auto table = new GuiTableComposition(); table->SetAlignmentToParent(Margin(0, 0, 0, 0)); table->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); table->SetRowsAndColumns(count * 2 + 1, 1); table->SetColumnOption(0, GuiCellOption::MinSizeOption()); table->SetRowOption(0, GuiCellOption::PercentageOption(1.0)); for (vint j = 0; j < count; j++) { table->SetRowOption(j * 2 + 1, GuiCellOption::MinSizeOption()); table->SetRowOption(j * 2 + 2, GuiCellOption::PercentageOption(1.0)); } for (vint j = 0; j < count; j++) { auto toolbar = new GuiToolstripToolBar(theme::ThemeName::ToolstripToolBar); toolbar->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); toolbars[toolbarIndex++] = toolbar; auto cell = new GuiCellComposition(); cell->SetSite(j * 2 + 1, 0, 1, 1); cell->AddChild(toolbar->GetBoundsComposition()); table->AddChild(cell); auto container = new GuiToolstripGroupContainer(theme::ThemeName::CustomControl); toolbar->GetToolstripItems().Add(container); containers[j] = container; } views[i] = new GuiResponsiveFixedComposition(); views[i]->AddChild(table); responsiveView->GetViews().Add(views[i]); } containerComposition->AddChild(responsiveView); } GuiRibbonToolstrips::~GuiRibbonToolstrips() { } collections::ObservableListBase& GuiRibbonToolstrips::GetGroups() { return groups; } #undef ARRLEN /*********************************************************************** GuiRibbonGallery::CommandExecutor ***********************************************************************/ GuiRibbonGallery::CommandExecutor::CommandExecutor(GuiRibbonGallery* _gallery) :gallery(_gallery) { } GuiRibbonGallery::CommandExecutor::~CommandExecutor() { } void GuiRibbonGallery::CommandExecutor::NotifyScrollUp() { gallery->RequestedScrollUp.Execute(gallery->GetNotifyEventArguments()); } void GuiRibbonGallery::CommandExecutor::NotifyScrollDown() { gallery->RequestedScrollDown.Execute(gallery->GetNotifyEventArguments()); } void GuiRibbonGallery::CommandExecutor::NotifyDropdown() { gallery->RequestedDropdown.Execute(gallery->GetNotifyEventArguments()); } /*********************************************************************** GuiRibbonGallery ***********************************************************************/ void GuiRibbonGallery::BeforeControlTemplateUninstalled_() { auto ct = GetControlTemplateObject(false); if (!ct) return; ct->SetCommands(nullptr); } void GuiRibbonGallery::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); ct->SetCommands(commandExecutor.Obj()); ct->SetScrollUpEnabled(scrollUpEnabled); ct->SetScrollDownEnabled(scrollDownEnabled); } GuiRibbonGallery::GuiRibbonGallery(theme::ThemeName themeName) :GuiControl(themeName) { commandExecutor = new CommandExecutor(this); ScrollUpEnabledChanged.SetAssociatedComposition(boundsComposition); ScrollDownEnabledChanged.SetAssociatedComposition(boundsComposition); RequestedScrollUp.SetAssociatedComposition(boundsComposition); RequestedScrollDown.SetAssociatedComposition(boundsComposition); RequestedDropdown.SetAssociatedComposition(boundsComposition); } GuiRibbonGallery::~GuiRibbonGallery() { } bool GuiRibbonGallery::GetScrollUpEnabled() { return scrollUpEnabled; } void GuiRibbonGallery::SetScrollUpEnabled(bool value) { if (scrollUpEnabled != value) { scrollUpEnabled = value; GetControlTemplateObject(true)->SetScrollUpEnabled(value); } } bool GuiRibbonGallery::GetScrollDownEnabled() { return scrollDownEnabled; } void GuiRibbonGallery::SetScrollDownEnabled(bool value) { if (scrollDownEnabled != value) { scrollDownEnabled = value; GetControlTemplateObject(true)->SetScrollDownEnabled(value); } } /*********************************************************************** GuiRibbonToolstripMenu ***********************************************************************/ void GuiRibbonToolstripMenu::BeforeControlTemplateUninstalled_() { auto ct = GetControlTemplateObject(false); if (!ct) return; if (auto cc = ct->GetContentComposition()) { cc->RemoveChild(contentComposition); } } void GuiRibbonToolstripMenu::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); if (auto cc = ct->GetContentComposition()) { cc->AddChild(contentComposition); } } GuiRibbonToolstripMenu::GuiRibbonToolstripMenu(theme::ThemeName themeName, GuiControl* owner) :GuiToolstripMenu(themeName, owner) { contentComposition = new GuiBoundsComposition(); contentComposition->SetAlignmentToParent(Margin(0, 0, 0, 0)); contentComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); } GuiRibbonToolstripMenu::~GuiRibbonToolstripMenu() { if (!contentComposition->GetParent()) { SafeDeleteComposition(contentComposition); } } compositions::GuiGraphicsComposition* GuiRibbonToolstripMenu::GetContentComposition() { return contentComposition; } } } } /*********************************************************************** .\CONTROLS\TOOLSTRIPPACKAGE\GUITOOLSTRIPMENU.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace collections; using namespace compositions; /*********************************************************************** GuiToolstripCollectionBase ***********************************************************************/ const wchar_t* const IToolstripUpdateLayoutInvoker::Identifier = L"vl::presentation::controls::IToolstripUpdateLayoutInvoker"; void GuiToolstripCollectionBase::InvokeUpdateLayout() { if(contentCallback) { contentCallback->UpdateLayout(); } } bool GuiToolstripCollectionBase::QueryInsert(vint index, GuiControl* const& child) { return !items.Contains(child) && !child->GetBoundsComposition()->GetParent(); } void GuiToolstripCollectionBase::BeforeRemove(vint index, GuiControl* const& child) { if (auto invoker = child->QueryTypedService()) { invoker->SetCallback(nullptr); } InvokeUpdateLayout(); } void GuiToolstripCollectionBase::AfterInsert(vint index, GuiControl* const& child) { if (auto invoker = child->QueryTypedService()) { invoker->SetCallback(contentCallback); } InvokeUpdateLayout(); } void GuiToolstripCollectionBase::AfterRemove(vint index, vint count) { InvokeUpdateLayout(); } GuiToolstripCollectionBase::GuiToolstripCollectionBase(IToolstripUpdateLayout* _contentCallback) :contentCallback(_contentCallback) { } GuiToolstripCollectionBase::~GuiToolstripCollectionBase() { } /*********************************************************************** GuiToolstripCollection ***********************************************************************/ void GuiToolstripCollection::UpdateItemVisibility(vint index, GuiControl* child) { auto stackItem = stackComposition->GetStackItems()[index]; if (child->GetVisible()) { stackItem->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); child->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); } else { stackItem->SetMinSizeLimitation(GuiGraphicsComposition::NoLimit); child->GetBoundsComposition()->SetAlignmentToParent(Margin(-1, -1, -1, -1)); } } void GuiToolstripCollection::OnItemVisibleChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { auto child = sender->GetRelatedControl(); vint index = IndexOf(child); UpdateItemVisibility(index, child); InvokeUpdateLayout(); } void GuiToolstripCollection::BeforeRemove(vint index, GuiControl* const& child) { GuiStackItemComposition* stackItem = stackComposition->GetStackItems().Get(index); auto eventHandler = eventHandlers[index]; child->VisibleChanged.Detach(eventHandler); eventHandlers.RemoveAt(index); GuiToolstripCollectionBase::BeforeRemove(index, child); } void GuiToolstripCollection::AfterInsert(vint index, GuiControl* const& child) { { GuiStackItemComposition* stackItem = new GuiStackItemComposition; stackItem->AddChild(child->GetBoundsComposition()); stackComposition->InsertStackItem(index, stackItem); } { auto eventHandler = child->VisibleChanged.AttachMethod(this, &GuiToolstripCollection::OnItemVisibleChanged); eventHandlers.Insert(index, eventHandler); } UpdateItemVisibility(index, child); GuiToolstripCollectionBase::AfterInsert(index, child); } void GuiToolstripCollection::AfterRemove(vint index, vint count) { for (vint i = 0; i < count; i++) { auto stackItem = stackComposition->GetStackItems().Get(index); stackComposition->RemoveChild(stackItem); SafeDeleteComposition(stackItem); } GuiToolstripCollectionBase::AfterRemove(index, count); } GuiToolstripCollection::GuiToolstripCollection(IToolstripUpdateLayout* _contentCallback, compositions::GuiStackComposition* _stackComposition) :GuiToolstripCollectionBase(_contentCallback) ,stackComposition(_stackComposition) { } GuiToolstripCollection::~GuiToolstripCollection() { } /*********************************************************************** GuiToolstripMenu ***********************************************************************/ void GuiToolstripMenu::UpdateLayout() { sharedSizeRootComposition->ForceCalculateSizeImmediately(); } GuiToolstripMenu::GuiToolstripMenu(theme::ThemeName themeName, GuiControl* _owner) :GuiMenu(themeName, _owner) { sharedSizeRootComposition = new GuiSharedSizeRootComposition(); sharedSizeRootComposition->SetAlignmentToParent(Margin(0, 0, 0, 0)); sharedSizeRootComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); containerComposition->AddChild(sharedSizeRootComposition); stackComposition=new GuiStackComposition; stackComposition->SetDirection(GuiStackComposition::Vertical); stackComposition->SetAlignmentToParent(Margin(0, 0, 0, 0)); stackComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); sharedSizeRootComposition->AddChild(stackComposition); toolstripItems = new GuiToolstripCollection(this, stackComposition); } GuiToolstripMenu::~GuiToolstripMenu() { } collections::ObservableListBase& GuiToolstripMenu::GetToolstripItems() { return *toolstripItems.Obj(); } /*********************************************************************** GuiToolstripMenuBar ***********************************************************************/ GuiToolstripMenuBar::GuiToolstripMenuBar(theme::ThemeName themeName) :GuiMenuBar(themeName) { stackComposition=new GuiStackComposition; stackComposition->SetDirection(GuiStackComposition::Horizontal); stackComposition->SetAlignmentToParent(Margin(0, 0, 0, 0)); stackComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); containerComposition->AddChild(stackComposition); toolstripItems=new GuiToolstripCollection(nullptr, stackComposition); } GuiToolstripMenuBar::~GuiToolstripMenuBar() { } collections::ObservableListBase& GuiToolstripMenuBar::GetToolstripItems() { return *toolstripItems.Obj(); } /*********************************************************************** GuiToolstripToolBar ***********************************************************************/ GuiToolstripToolBar::GuiToolstripToolBar(theme::ThemeName themeName) :GuiControl(themeName) { stackComposition=new GuiStackComposition; stackComposition->SetDirection(GuiStackComposition::Horizontal); stackComposition->SetAlignmentToParent(Margin(0, 0, 0, 0)); stackComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); containerComposition->AddChild(stackComposition); toolstripItems=new GuiToolstripCollection(nullptr, stackComposition); } GuiToolstripToolBar::~GuiToolstripToolBar() { } collections::ObservableListBase& GuiToolstripToolBar::GetToolstripItems() { return *toolstripItems.Obj(); } /*********************************************************************** GuiToolstripButton ***********************************************************************/ void GuiToolstripButton::SetCallback(IToolstripUpdateLayout* _callback) { callback = _callback; } void GuiToolstripButton::UpdateCommandContent() { if(command) { SetLargeImage(command->GetLargeImage()); SetImage(command->GetImage()); SetText(command->GetText()); SetEnabled(command->GetEnabled()); SetSelected(command->GetSelected()); if(command->GetShortcut()) { SetShortcutText(command->GetShortcut()->GetName()); } else { SetShortcutText(L""); } } else { SetLargeImage(nullptr); SetImage(nullptr); SetText(L""); SetEnabled(true); SetSelected(false); SetShortcutText(L""); } } void GuiToolstripButton::OnLayoutAwaredPropertyChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if (callback) { callback->UpdateLayout(); } } void GuiToolstripButton::OnClicked(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { if(command) { command->Executed.ExecuteWithNewSender(arguments, sender); } } void GuiToolstripButton::OnCommandDescriptionChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { UpdateCommandContent(); } GuiToolstripButton::GuiToolstripButton(theme::ThemeName themeName) :GuiMenuButton(themeName) ,command(0) { Clicked.AttachMethod(this, &GuiToolstripButton::OnClicked); TextChanged.AttachMethod(this, &GuiToolstripButton::OnLayoutAwaredPropertyChanged); ShortcutTextChanged.AttachMethod(this, &GuiToolstripButton::OnLayoutAwaredPropertyChanged); } GuiToolstripButton::~GuiToolstripButton() { } GuiToolstripCommand* GuiToolstripButton::GetCommand() { return command; } void GuiToolstripButton::SetCommand(GuiToolstripCommand* value) { if(command!=value) { if(command) { command->DescriptionChanged.Detach(descriptionChangedHandler); } command=0; descriptionChangedHandler=0; if(value) { command=value; descriptionChangedHandler=command->DescriptionChanged.AttachMethod(this, &GuiToolstripButton::OnCommandDescriptionChanged); } UpdateCommandContent(); } } GuiToolstripMenu* GuiToolstripButton::GetToolstripSubMenu() { return dynamic_cast(GetSubMenu()); } GuiToolstripMenu* GuiToolstripButton::EnsureToolstripSubMenu() { if (!GetSubMenu()) { CreateToolstripSubMenu({}); } return dynamic_cast(GetSubMenu()); } void GuiToolstripButton::CreateToolstripSubMenu(TemplateProperty subMenuTemplate) { if (!subMenu) { auto newSubMenu = new GuiToolstripMenu(theme::ThemeName::Menu, this); if (subMenuTemplate) { newSubMenu->SetControlTemplate(subMenuTemplate); } else { newSubMenu->SetControlTemplate(GetControlTemplateObject(true)->GetSubMenuTemplate()); } SetSubMenu(newSubMenu, true); } } IDescriptable* GuiToolstripButton::QueryService(const WString& identifier) { if (identifier == IToolstripUpdateLayoutInvoker::Identifier) { return (IToolstripUpdateLayoutInvoker*)this; } else { return GuiMenuButton::QueryService(identifier); } } /*********************************************************************** GuiToolstripNestedContainer ***********************************************************************/ void GuiToolstripNestedContainer::UpdateLayout() { if (callback) { callback->UpdateLayout(); } } void GuiToolstripNestedContainer::SetCallback(IToolstripUpdateLayout* _callback) { callback = _callback; } GuiToolstripNestedContainer::GuiToolstripNestedContainer(theme::ThemeName themeName) :GuiControl(themeName) { } GuiToolstripNestedContainer::~GuiToolstripNestedContainer() { } IDescriptable* GuiToolstripNestedContainer::QueryService(const WString& identifier) { if (identifier == IToolstripUpdateLayoutInvoker::Identifier) { return (IToolstripUpdateLayoutInvoker*)this; } else { return GuiControl::QueryService(identifier); } } /*********************************************************************** GuiToolstripGroupContainer::GroupCollection ***********************************************************************/ void GuiToolstripGroupContainer::GroupCollection::BeforeRemove(vint index, GuiControl* const& child) { auto controlStackItem = container->stackComposition->GetStackItems()[index * 2]; CHECK_ERROR(controlStackItem->RemoveChild(child->GetBoundsComposition()), L"GuiToolstripGroupContainer::GroupCollection::BeforeRemove(vint, GuiControl* const&)#Internal error"); } void GuiToolstripGroupContainer::GroupCollection::AfterInsert(vint index, GuiControl* const& child) { auto controlStackItem = new GuiStackItemComposition; child->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); CHECK_ERROR(controlStackItem->AddChild(child->GetBoundsComposition()), L"GuiToolstripGroupContainer::GroupCollection::AfterInsert(vint, GuiControl* const&)#Internal error"); if (container->stackComposition->GetStackItems().Count() > 0) { auto splitterStackItem = new GuiStackItemComposition; auto splitter = new GuiControl(container->splitterThemeName); if (splitterTemplate) { splitter->SetControlTemplate(splitterTemplate); } splitter->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); splitterStackItem->AddChild(splitter->GetBoundsComposition()); container->stackComposition->InsertStackItem(index == 0 ? 0 : index * 2 - 1, splitterStackItem); } container->stackComposition->InsertStackItem(index * 2, controlStackItem); GuiToolstripCollectionBase::AfterInsert(index, child); } void GuiToolstripGroupContainer::GroupCollection::AfterRemove(vint index, vint count) { vint min = index * 2; vint max = (index + count - 1) * 2; for (vint i = min; i <= max; i++) { auto stackItem = container->stackComposition->GetStackItems()[min]; container->stackComposition->RemoveChild(stackItem); SafeDeleteComposition(stackItem); } GuiToolstripCollectionBase::AfterRemove(index, count); } GuiToolstripGroupContainer::GroupCollection::GroupCollection(GuiToolstripGroupContainer* _container) :GuiToolstripCollectionBase(_container) , container(_container) { } GuiToolstripGroupContainer::GroupCollection::~GroupCollection() { } GuiToolstripGroupContainer::ControlTemplatePropertyType GuiToolstripGroupContainer::GroupCollection::GetSplitterTemplate() { return splitterTemplate; } void GuiToolstripGroupContainer::GroupCollection::SetSplitterTemplate(const ControlTemplatePropertyType& value) { splitterTemplate = value; RebuildSplitters(); } void GuiToolstripGroupContainer::GroupCollection::RebuildSplitters() { auto stack = container->stackComposition; vint count = stack->GetStackItems().Count(); for (vint i = 1; i < count; i += 2) { auto stackItem = stack->GetStackItems()[i]; { auto control = stackItem->Children()[0]->GetAssociatedControl(); CHECK_ERROR(control != nullptr, L"GuiToolstripGroupContainer::GroupCollection::RebuildSplitters()#Internal error"); stackItem->RemoveChild(control->GetBoundsComposition()); delete control; } { auto control = new GuiControl(container->splitterThemeName); if (splitterTemplate) { control->SetControlTemplate(splitterTemplate); } control->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); stackItem->AddChild(control->GetBoundsComposition()); } } } /*********************************************************************** GuiToolstripGroupContainer ***********************************************************************/ void GuiToolstripGroupContainer::OnParentLineChanged() { auto direction = GuiStackComposition::Horizontal; if (auto service = QueryTypedService()) { if (service->GetPreferredDirection() == IGuiMenuService::Vertical) { direction = GuiStackComposition::Vertical; } } if (direction != stackComposition->GetDirection()) { if (direction == GuiStackComposition::Vertical) { splitterThemeName = theme::ThemeName::MenuSplitter; } else { splitterThemeName = theme::ThemeName::ToolstripSplitter; } stackComposition->SetDirection(direction); splitterThemeName = splitterThemeName; groupCollection->RebuildSplitters(); UpdateLayout(); } GuiControl::OnParentLineChanged(); } GuiToolstripGroupContainer::GuiToolstripGroupContainer(theme::ThemeName themeName) :GuiToolstripNestedContainer(themeName) , splitterThemeName(theme::ThemeName::ToolstripSplitter) { stackComposition = new GuiStackComposition; stackComposition->SetDirection(GuiStackComposition::Horizontal); stackComposition->SetAlignmentToParent(Margin(0, 0, 0, 0)); stackComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); containerComposition->AddChild(stackComposition); groupCollection = new GroupCollection(this); } GuiToolstripGroupContainer::~GuiToolstripGroupContainer() { } GuiToolstripGroupContainer::ControlTemplatePropertyType GuiToolstripGroupContainer::GetSplitterTemplate() { return groupCollection->GetSplitterTemplate(); } void GuiToolstripGroupContainer::SetSplitterTemplate(const ControlTemplatePropertyType& value) { groupCollection->SetSplitterTemplate(value); } collections::ObservableListBase& GuiToolstripGroupContainer::GetToolstripItems() { return *groupCollection.Obj(); } /*********************************************************************** GuiToolstripGroup ***********************************************************************/ void GuiToolstripGroup::OnParentLineChanged() { auto direction = GuiStackComposition::Horizontal; if (auto service = QueryTypedService()) { if (service->GetPreferredDirection() == IGuiMenuService::Vertical) { direction = GuiStackComposition::Vertical; } } if (direction != stackComposition->GetDirection()) { stackComposition->SetDirection(direction); UpdateLayout(); } GuiControl::OnParentLineChanged(); } GuiToolstripGroup::GuiToolstripGroup(theme::ThemeName themeName) :GuiToolstripNestedContainer(themeName) { stackComposition = new GuiStackComposition; stackComposition->SetDirection(GuiStackComposition::Horizontal); stackComposition->SetAlignmentToParent(Margin(0, 0, 0, 0)); stackComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); containerComposition->AddChild(stackComposition); toolstripItems = new GuiToolstripCollection(nullptr, stackComposition); } GuiToolstripGroup::~GuiToolstripGroup() { } collections::ObservableListBase& GuiToolstripGroup::GetToolstripItems() { return *toolstripItems.Obj(); } } } } /*********************************************************************** .\CONTROLS\TOOLSTRIPPACKAGE\GUIRIBBONGALLERYLIST.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace reflection::description; using namespace collections; using namespace compositions; using namespace templates; namespace list { /*********************************************************************** list::GalleryGroup ***********************************************************************/ GalleryGroup::GalleryGroup() { } GalleryGroup::~GalleryGroup() { if (eventHandler) { itemValues.Cast()->ItemChanged.Remove(eventHandler); } } WString GalleryGroup::GetName() { return name; } Ptr GalleryGroup::GetItemValues() { return itemValues; } /*********************************************************************** list::GroupedDataSource ***********************************************************************/ void GroupedDataSource::RebuildItemSource() { ignoreGroupChanged = true; joinedItemSource.Clear(); groupedItemSource.Clear(); cachedGroupItemCounts.Clear(); ignoreGroupChanged = false; if (itemSource) { if (GetGroupEnabled()) { FOREACH_INDEXER(Value, groupValue, index, GetLazyList(itemSource)) { auto group = MakePtr(); group->name = titleProperty(groupValue); group->itemValues = GetChildren(childrenProperty(groupValue)); AttachGroupChanged(group, index); groupedItemSource.Add(group); } } else { auto group = MakePtr(); group->itemValues = GetChildren(itemSource); AttachGroupChanged(group, 0); groupedItemSource.Add(group); } } } Ptr GroupedDataSource::GetChildren(Ptr children) { if (!children) { return nullptr; } else if (auto list = children.Cast()) { return list; } else { return IValueList::Create(GetLazyList(children)); } } void GroupedDataSource::AttachGroupChanged(Ptr group, vint index) { if (auto observable = group->itemValues.Cast()) { group->eventHandler = observable->ItemChanged.Add([this, index](vint start, vint oldCount, vint newCount) { OnGroupItemChanged(index, start, oldCount, newCount); }); } } void GroupedDataSource::OnGroupChanged(vint start, vint oldCount, vint newCount) { if (!ignoreGroupChanged) { for (vint i = 0; i < oldCount; i++) { RemoveGroupFromJoined(start); } for (vint i = 0; i < newCount; i++) { InsertGroupToJoined(start + i); } } } void GroupedDataSource::OnGroupItemChanged(vint index, vint start, vint oldCount, vint newCount) { if (!ignoreGroupChanged) { vint countBeforeGroup = GetCountBeforeGroup(index); vint joinedIndex = countBeforeGroup + start; vint minCount = oldCount < newCount ? oldCount : newCount; auto itemValues = groupedItemSource[index]->itemValues; for (vint i = 0; i < minCount; i++) { joinedItemSource.Set(joinedIndex + i, itemValues->Get(start + i)); } if (oldCount < newCount) { for (vint i = minCount; i < newCount; i++) { joinedItemSource.Insert(joinedIndex + i, itemValues->Get(start + i)); } } else if (oldCount > newCount) { for (vint i = minCount; i < oldCount; i++) { joinedItemSource.RemoveAt(joinedIndex + i); } } cachedGroupItemCounts[index] += newCount - oldCount; } } vint GroupedDataSource::GetCountBeforeGroup(vint index) { vint count = 0; for (vint i = 0; i < index; i++) { count += cachedGroupItemCounts[i]; } return count; } void GroupedDataSource::InsertGroupToJoined(vint index) { vint countBeforeGroup = GetCountBeforeGroup(index); auto group = groupedItemSource[index]; vint itemCount = group->itemValues ? group->itemValues->GetCount() : 0; cachedGroupItemCounts.Insert(index, itemCount); if (itemCount > 0) { for (vint i = 0; i < itemCount; i++) { joinedItemSource.Insert(countBeforeGroup + i, group->itemValues->Get(i)); } } } void GroupedDataSource::RemoveGroupFromJoined(vint index) { vint countBeforeGroup = GetCountBeforeGroup(index); joinedItemSource.RemoveRange(countBeforeGroup, cachedGroupItemCounts[index]); cachedGroupItemCounts.RemoveAt(index); } GroupedDataSource::GroupedDataSource(compositions::GuiGraphicsComposition* _associatedComposition) :associatedComposition(_associatedComposition) { GroupEnabledChanged.SetAssociatedComposition(associatedComposition); GroupTitlePropertyChanged.SetAssociatedComposition(associatedComposition); GroupChildrenPropertyChanged.SetAssociatedComposition(associatedComposition); groupChangedHandler = groupedItemSource.GetWrapper()->ItemChanged.Add(this, &GroupedDataSource::OnGroupChanged); } GroupedDataSource::~GroupedDataSource() { joinedItemSource.GetWrapper()->ItemChanged.Remove(groupChangedHandler); } Ptr GroupedDataSource::GetItemSource() { return itemSource; } void GroupedDataSource::SetItemSource(Ptr value) { if (itemSource != value) { itemSource = value; RebuildItemSource(); } } bool GroupedDataSource::GetGroupEnabled() { return titleProperty && childrenProperty; } ItemProperty GroupedDataSource::GetGroupTitleProperty() { return titleProperty; } void GroupedDataSource::SetGroupTitleProperty(const ItemProperty& value) { if (titleProperty != value) { titleProperty = value; GroupTitlePropertyChanged.Execute(GuiEventArgs(associatedComposition)); GroupEnabledChanged.Execute(GuiEventArgs(associatedComposition)); RebuildItemSource(); } } ItemProperty> GroupedDataSource::GetGroupChildrenProperty() { return childrenProperty; } void GroupedDataSource::SetGroupChildrenProperty(const ItemProperty>& value) { if (childrenProperty != value) { childrenProperty = value; GroupChildrenPropertyChanged.Execute(GuiEventArgs(associatedComposition)); GroupEnabledChanged.Execute(GuiEventArgs(associatedComposition)); RebuildItemSource(); } } const GroupedDataSource::GalleryGroupList& GroupedDataSource::GetGroups() { return groupedItemSource; } } /*********************************************************************** GuiBindableRibbonGalleryList ***********************************************************************/ void GuiBindableRibbonGalleryList::BeforeControlTemplateUninstalled_() { } void GuiBindableRibbonGalleryList::AfterControlTemplateInstalled_(bool initialize) { auto ct = GetControlTemplateObject(true); itemList->SetControlTemplate(ct->GetItemListTemplate()); subMenu->SetControlTemplate(ct->GetMenuTemplate()); groupContainer->SetControlTemplate(ct->GetGroupContainerTemplate()); MenuResetGroupTemplate(); UpdateLayoutSizeOffset(); } void GuiBindableRibbonGalleryList::UpdateLayoutSizeOffset() { auto cSize = itemList->GetContainerComposition()->GetBounds(); auto bSize = itemList->GetBoundsComposition()->GetBounds(); layout->SetSizeOffset(Size(bSize.Width() - cSize.Width(), bSize.Height() - cSize.Height())); if (layout->GetItemWidth() > 0) { vint columns = layout->GetVisibleItemCount(); if (columns == 0) columns = 1; vint rows = (visibleItemCount + columns - 1) / columns; vint height = (vint)(layout->GetBounds().Height()*(rows + 0.5)); groupContainer->GetBoundsComposition()->SetPreferredMinSize(Size(0, height)); } else { groupContainer->GetBoundsComposition()->SetPreferredMinSize(Size(0, 0)); } } void GuiBindableRibbonGalleryList::OnItemListSelectionChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { auto pos = IndexToGalleryPos(itemList->GetSelectedItemIndex()); if (pos.group != -1 && pos.item != -1) { for (vint i = 0; i < groupedItemSource.Count(); i++) { auto group = groupedItemSource[i]; if (group->GetItemValues()) { vint count = group->GetItemValues()->GetCount(); for (vint j = 0; j < count; j++) { auto background = MenuGetGroupItemBackground(i, j); background->SetSelected(pos.group == i && pos.item == j); } } } } if (!skipItemAppliedEvent && itemList->GetSelectedItemIndex() != -1) { GuiItemEventArgs itemAppliedArgs(boundsComposition); itemAppliedArgs.itemIndex = itemList->GetSelectedItemIndex(); ItemApplied.Execute(itemAppliedArgs); } SelectionChanged.Execute(GetNotifyEventArguments()); } void GuiBindableRibbonGalleryList::OnItemListItemMouseEnter(compositions::GuiGraphicsComposition* sender, compositions::GuiItemEventArgs& arguments) { StartPreview(arguments.itemIndex); } void GuiBindableRibbonGalleryList::OnItemListItemMouseLeave(compositions::GuiGraphicsComposition* sender, compositions::GuiItemEventArgs& arguments) { StopPreview(arguments.itemIndex); } void GuiBindableRibbonGalleryList::OnBoundsChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { UpdateLayoutSizeOffset(); auto bounds = boundsComposition->GetBounds(); subMenu->GetBoundsComposition()->SetPreferredMinSize(Size(bounds.Width() + 20, 1)); for (vint i = 0; i < groupedItemSource.Count(); i++) { auto group = groupedItemSource[i]; if (group->GetItemValues()) { vint count = group->GetItemValues()->GetCount(); for (vint j = 0; j < count; j++) { auto background = MenuGetGroupItemBackground(i, j); background->GetBoundsComposition()->SetPreferredMinSize(Size(0, bounds.Height())); } } } } void GuiBindableRibbonGalleryList::OnRequestedDropdown(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { subMenu->ShowPopup(this, Point(0, 0)); } void GuiBindableRibbonGalleryList::OnRequestedScrollUp(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { itemListArranger->ScrollUp(); } void GuiBindableRibbonGalleryList::OnRequestedScrollDown(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { itemListArranger->ScrollDown(); } void GuiBindableRibbonGalleryList::MenuResetGroupTemplate() { groupStack->SetItemTemplate([this](const Value& groupValue)->GuiTemplate* { auto group = UnboxValue>(groupValue); auto groupTemplate = new GuiTemplate; groupTemplate->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); auto groupContentStack = new GuiStackComposition; groupContentStack->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); groupContentStack->SetAlignmentToParent(Margin(0, 0, 0, 0)); groupContentStack->SetDirection(GuiStackComposition::Vertical); { auto item = new GuiStackItemComposition; groupContentStack->AddChild(item); auto header = new GuiControl(theme::ThemeName::RibbonToolstripHeader); header->SetControlTemplate(GetControlTemplateObject(true)->GetHeaderTemplate()); header->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); header->SetText(group->GetName()); item->AddChild(header->GetBoundsComposition()); } if (itemStyle) { auto item = new GuiStackItemComposition; item->SetPreferredMinSize(Size(1, 1)); groupContentStack->AddChild(item); auto groupItemFlow = new GuiRepeatFlowComposition(); groupItemFlow->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); groupItemFlow->SetAlignmentToParent(Margin(0, 0, 0, 0)); groupItemFlow->SetItemSource(group->GetItemValues()); groupItemFlow->SetItemTemplate([=](const Value& groupItemValue)->GuiTemplate* { auto groupItemTemplate = new GuiTemplate; groupItemTemplate->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); auto backgroundButton = new GuiSelectableButton(theme::ThemeName::ListItemBackground); if (auto style = GetControlTemplateObject(true)->GetBackgroundTemplate()) { backgroundButton->SetControlTemplate(style); } backgroundButton->SetAutoSelection(false); backgroundButton->Clicked.AttachLambda([=](GuiGraphicsComposition* sender, GuiEventArgs& arguments) { auto groupIndex = groupStack->GetStackItems().IndexOf(dynamic_cast(groupTemplate->GetParent())); auto itemIndex = groupItemFlow->GetFlowItems().IndexOf(dynamic_cast(groupItemTemplate->GetParent())); auto index = GalleryPosToIndex({ groupIndex,itemIndex }); itemList->SetSelected(index, true); itemList->EnsureItemVisible(index); subMenu->Close(); }); backgroundButton->GetBoundsComposition()->GetEventReceiver()->mouseEnter.AttachLambda([=](GuiGraphicsComposition* sender, GuiEventArgs& arguments) { auto groupIndex = groupStack->GetStackItems().IndexOf(dynamic_cast(groupTemplate->GetParent())); auto itemIndex = groupItemFlow->GetFlowItems().IndexOf(dynamic_cast(groupItemTemplate->GetParent())); auto index = GalleryPosToIndex({ groupIndex,itemIndex }); StartPreview(index); }); backgroundButton->GetBoundsComposition()->GetEventReceiver()->mouseLeave.AttachLambda([=](GuiGraphicsComposition* sender, GuiEventArgs& arguments) { auto groupIndex = groupStack->GetStackItems().IndexOf(dynamic_cast(groupTemplate->GetParent())); auto itemIndex = groupItemFlow->GetFlowItems().IndexOf(dynamic_cast(groupItemTemplate->GetParent())); auto index = GalleryPosToIndex({ groupIndex,itemIndex }); StopPreview(index); }); groupItemTemplate->AddChild(backgroundButton->GetBoundsComposition()); auto itemTemplate = itemStyle(groupItemValue); itemTemplate->SetAlignmentToParent(Margin(0, 0, 0, 0)); backgroundButton->GetContainerComposition()->AddChild(itemTemplate); return groupItemTemplate; }); item->AddChild(groupItemFlow); } groupTemplate->AddChild(groupContentStack); return groupTemplate; }); } GuiControl* GuiBindableRibbonGalleryList::MenuGetGroupHeader(vint groupIndex) { CHECK_ERROR(0 <= groupIndex && groupIndex < groupedItemSource.Count(), L"GuiBindableRibbonGalleryList::MenuGetGroupHeader(vint)#Group index out of range"); auto stackItem = groupStack->GetStackItems()[groupIndex]; auto groupTemplate = stackItem->Children()[0]; auto groupContentStack = dynamic_cast(groupTemplate->Children()[0]); auto groupHeaderItem = groupContentStack->GetStackItems()[0]; auto groupHeader = groupHeaderItem->Children()[0]->GetAssociatedControl(); CHECK_ERROR(groupHeader, L"GuiBindableRibbonGalleryList::MenuGetGroupHeader(vint)#Internal error."); return groupHeader; } compositions::GuiRepeatFlowComposition* GuiBindableRibbonGalleryList::MenuGetGroupFlow(vint groupIndex) { CHECK_ERROR(0 <= groupIndex && groupIndex < groupedItemSource.Count(), L"GuiBindableRibbonGalleryList::MenuGetGroupFlow(vint)#Group index out of range"); if (!itemStyle) return nullptr; auto stackItem = groupStack->GetStackItems()[groupIndex]; auto groupTemplate = stackItem->Children()[0]; auto groupContentStack = dynamic_cast(groupTemplate->Children()[0]); auto groupContentItem = groupContentStack->GetStackItems()[1]; auto groupFlow = dynamic_cast(groupContentItem->Children()[0]); CHECK_ERROR(groupFlow, L"GuiBindableRibbonGalleryList::MenuGetGroupHeader(vint)#Internal error."); return groupFlow; } GuiSelectableButton* GuiBindableRibbonGalleryList::MenuGetGroupItemBackground(vint groupIndex, vint itemIndex) { CHECK_ERROR(0 <= groupIndex && groupIndex < groupedItemSource.Count(), L"GuiBindableRibbonGalleryList::MenuGetGroupItemBackground(vint, vint)#Group index out of range"); auto group = groupedItemSource[groupIndex]; CHECK_ERROR(group->GetItemValues() && 0 <= itemIndex && itemIndex < group->GetItemValues()->GetCount(), L"GuiBindableRibbonGalleryList::MenuGetGroupHeader(vint, vint)#Item index out of range"); auto groupFlow = MenuGetGroupFlow(groupIndex); auto groupFlowItem = groupFlow->GetFlowItems()[itemIndex]; auto groupItemTemplate = groupFlowItem->Children()[0]; auto groupItemBackground = dynamic_cast(groupItemTemplate->Children()[0]->GetAssociatedControl()); CHECK_ERROR(groupItemBackground, L"GuiBindableRibbonGalleryList::MenuGetGroupHeader(vint, vint)#Internal error."); return groupItemBackground; } void GuiBindableRibbonGalleryList::StartPreview(vint index) { if (index != itemList->GetSelectedItemIndex()) { GuiItemEventArgs previewArgs(boundsComposition); previewArgs.itemIndex = index; PreviewStarted.Execute(previewArgs); } } void GuiBindableRibbonGalleryList::StopPreview(vint index) { if (index != itemList->GetSelectedItemIndex()) { GuiItemEventArgs previewArgs(boundsComposition); previewArgs.itemIndex = index; PreviewStopped.Execute(previewArgs); } } GuiMenu* GuiBindableRibbonGalleryList::ProvideDropdownMenu() { return GetSubMenu(); } GuiBindableRibbonGalleryList::GuiBindableRibbonGalleryList(theme::ThemeName themeName) :GuiRibbonGallery(themeName) , GroupedDataSource(boundsComposition) { ItemTemplateChanged.SetAssociatedComposition(boundsComposition); SelectionChanged.SetAssociatedComposition(boundsComposition); PreviewStarted.SetAssociatedComposition(boundsComposition); PreviewStopped.SetAssociatedComposition(boundsComposition); ItemApplied.SetAssociatedComposition(boundsComposition); subMenu = new GuiRibbonToolstripMenu(theme::ThemeName::RibbonToolstripMenu, this); { layout = new ribbon_impl::GalleryResponsiveLayout; layout->SetAlignmentToParent(Margin(0, 0, 0, 0)); containerComposition->AddChild(layout); itemListArranger = new ribbon_impl::GalleryItemArranger(this); itemList = new GuiBindableTextList(theme::ThemeName::RibbonGalleryItemList); itemList->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); itemList->SetArranger(itemListArranger); itemList->SetItemSource(joinedItemSource.GetWrapper()); itemList->SelectionChanged.AttachMethod(this, &GuiBindableRibbonGalleryList::OnItemListSelectionChanged); itemList->ItemMouseEnter.AttachMethod(this, &GuiBindableRibbonGalleryList::OnItemListItemMouseEnter); itemList->ItemMouseLeave.AttachMethod(this, &GuiBindableRibbonGalleryList::OnItemListItemMouseLeave); layout->AddChild(itemList->GetBoundsComposition()); } { groupContainer = new GuiScrollContainer(theme::ThemeName::ScrollView); groupContainer->SetHorizontalAlwaysVisible(false); groupContainer->SetVerticalAlwaysVisible(false); groupContainer->SetExtendToFullWidth(true); groupContainer->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); subMenu->GetContentComposition()->AddChild(groupContainer->GetBoundsComposition()); groupStack = new GuiRepeatStackComposition(); groupStack->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren); groupStack->SetAlignmentToParent(Margin(0, 0, 0, 0)); groupStack->SetDirection(GuiStackComposition::Vertical); groupStack->SetItemSource(groupedItemSource.GetWrapper()); groupContainer->GetContainerComposition()->AddChild(groupStack); MenuResetGroupTemplate(); } RequestedScrollUp.AttachMethod(this, &GuiBindableRibbonGalleryList::OnRequestedScrollUp); RequestedScrollDown.AttachMethod(this, &GuiBindableRibbonGalleryList::OnRequestedScrollDown); RequestedDropdown.AttachMethod(this, &GuiBindableRibbonGalleryList::OnRequestedDropdown); boundsComposition->BoundsChanged.AttachMethod(this, &GuiBindableRibbonGalleryList::OnBoundsChanged); itemListArranger->UnblockScrollUpdate(); } GuiBindableRibbonGalleryList::~GuiBindableRibbonGalleryList() { delete subMenu; } GuiBindableRibbonGalleryList::ItemStyleProperty GuiBindableRibbonGalleryList::GetItemTemplate() { return itemStyle; } void GuiBindableRibbonGalleryList::SetItemTemplate(ItemStyleProperty value) { if (itemStyle != value) { itemStyle = value; itemList->SetItemTemplate(value); ItemTemplateChanged.Execute(GetNotifyEventArguments()); } } GalleryPos GuiBindableRibbonGalleryList::IndexToGalleryPos(vint index) { if (0 <= index && index < joinedItemSource.Count()) { FOREACH_INDEXER(Ptr, group, groupIndex, groupedItemSource) { auto itemValues = group->GetItemValues(); vint itemCount = itemValues ? itemValues->GetCount() : 0; if (index >= itemCount) { index -= itemCount; } else { return GalleryPos(groupIndex, index); } } } return {}; } vint GuiBindableRibbonGalleryList::GalleryPosToIndex(GalleryPos pos) { if (0 <= pos.group && pos.group < groupedItemSource.Count()) { auto countBeforeGroup = GetCountBeforeGroup(pos.group); auto itemValues = groupedItemSource[pos.group]->GetItemValues(); vint itemCount = itemValues ? itemValues->GetCount() : 0; if (0 <= pos.item && pos.item < itemCount) { return countBeforeGroup + pos.item; } } return -1; } vint GuiBindableRibbonGalleryList::GetMinCount() { return layout->GetMinCount(); } void GuiBindableRibbonGalleryList::SetMinCount(vint value) { layout->SetMinCount(value); } vint GuiBindableRibbonGalleryList::GetMaxCount() { return layout->GetMaxCount(); } void GuiBindableRibbonGalleryList::SetMaxCount(vint value) { layout->SetMaxCount(value); } vint GuiBindableRibbonGalleryList::GetSelectedIndex() { return itemList->GetSelectedItemIndex(); } description::Value GuiBindableRibbonGalleryList::GetSelectedItem() { vint index = itemList->GetSelectedItemIndex(); if (index == -1) return Value(); auto pos = IndexToGalleryPos(index); return groupedItemSource[pos.group]->GetItemValues()->Get(pos.item); } void GuiBindableRibbonGalleryList::ApplyItem(vint index) { if (index == -1) { itemList->ClearSelection(); } else { itemList->SetSelected(index, true); itemList->EnsureItemVisible(index); } } void GuiBindableRibbonGalleryList::SelectItem(vint index) { skipItemAppliedEvent = true; ApplyItem(index); skipItemAppliedEvent = false; } vint GuiBindableRibbonGalleryList::GetVisibleItemCount() { return visibleItemCount; } void GuiBindableRibbonGalleryList::SetVisibleItemCount(vint value) { if (visibleItemCount != value) { visibleItemCount = value; UpdateLayoutSizeOffset(); } } GuiToolstripMenu* GuiBindableRibbonGalleryList::GetSubMenu() { return subMenu; } IDescriptable* GuiBindableRibbonGalleryList::QueryService(const WString& identifier) { if (identifier == IGuiMenuDropdownProvider::Identifier) { return (IGuiMenuDropdownProvider*)this; } else { return GuiRibbonGallery::QueryService(identifier); } } } } } /*********************************************************************** .\CONTROLS\TOOLSTRIPPACKAGE\GUIRIBBONIMPL.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace compositions; /*********************************************************************** GalleryItemArranger ***********************************************************************/ namespace ribbon_impl { void GalleryItemArranger::BeginPlaceItem(bool forMoving, Rect newBounds, vint& newStartIndex) { if (forMoving) { pim_itemWidth = itemWidth; newStartIndex = firstIndex; } } void GalleryItemArranger::PlaceItem(bool forMoving, vint index, ItemStyleRecord style, Rect viewBounds, Rect& bounds, Margin& alignmentToParent) { alignmentToParent = Margin(-1, 0, -1, 0); bounds = Rect(Point((index - firstIndex) * itemWidth, 0), Size(itemWidth, 0)); if (forMoving) { vint styleWidth = callback->GetStylePreferredSize(GetStyleBounds(style)).x; if (pim_itemWidth < styleWidth) { pim_itemWidth = styleWidth; } } } bool GalleryItemArranger::IsItemOutOfViewBounds(vint index, ItemStyleRecord style, Rect bounds, Rect viewBounds) { return bounds.Right() + pim_itemWidth > viewBounds.Right(); } bool GalleryItemArranger::EndPlaceItem(bool forMoving, Rect newBounds, vint newStartIndex) { bool result = false; if (forMoving) { if (pim_itemWidth != itemWidth) { itemWidth = pim_itemWidth; result = true; } } if (!blockScrollUpdate) { UnblockScrollUpdate(); } return result; } void GalleryItemArranger::InvalidateItemSizeCache() { itemWidth = 1; } Size GalleryItemArranger::OnCalculateTotalSize() { return Size(1, 1); } GalleryItemArranger::GalleryItemArranger(GuiBindableRibbonGalleryList* _owner) :owner(_owner) { } GalleryItemArranger::~GalleryItemArranger() { } vint GalleryItemArranger::FindItem(vint itemIndex, compositions::KeyDirection key) { vint count = itemProvider->Count(); vint groupCount = viewBounds.Width() / itemWidth; switch (key) { case KeyDirection::Left: itemIndex--; break; case KeyDirection::Right: itemIndex++; break; case KeyDirection::Home: itemIndex = 0; break; case KeyDirection::End: itemIndex = count; break; case KeyDirection::PageUp: itemIndex -= groupCount; break; case KeyDirection::PageDown: itemIndex += groupCount; break; default: return -1; } if (itemIndex < 0) return 0; else if (itemIndex >= count) return count - 1; else return itemIndex; } bool GalleryItemArranger::EnsureItemVisible(vint itemIndex) { if (callback && 0 <= itemIndex && itemIndex < itemProvider->Count()) { vint groupCount = viewBounds.Width() / itemWidth; if (itemIndex < firstIndex) { firstIndex = itemIndex; callback->OnTotalSizeChanged(); } else if (itemIndex >= firstIndex + groupCount) { firstIndex = itemIndex - groupCount + 1; callback->OnTotalSizeChanged(); } return true; } return false; } Size GalleryItemArranger::GetAdoptedSize(Size expectedSize) { return Size(1, 1); } void GalleryItemArranger::ScrollUp() { vint count = itemProvider->Count(); vint groupCount = viewBounds.Width() / itemWidth; if (count > groupCount) { firstIndex -= groupCount; if (firstIndex < 0) { firstIndex = 0; } if (callback) { callback->OnTotalSizeChanged(); } } } void GalleryItemArranger::ScrollDown() { vint count = itemProvider->Count(); vint groupCount = viewBounds.Width() / itemWidth; if (count > groupCount) { firstIndex += groupCount; if (firstIndex > count - groupCount) { firstIndex = count - groupCount; } if (callback) { callback->OnTotalSizeChanged(); } } } void GalleryItemArranger::UnblockScrollUpdate() { blockScrollUpdate = false; vint count = itemProvider->Count(); vint groupCount = viewBounds.Width() / pim_itemWidth; owner->SetScrollUpEnabled(firstIndex > 0); owner->SetScrollDownEnabled(firstIndex + groupCount < count); if (owner->layout->GetItemWidth() != pim_itemWidth) { owner->layout->SetItemWidth(pim_itemWidth); owner->UpdateLayoutSizeOffset(); } } /*********************************************************************** GalleryResponsiveLayout ***********************************************************************/ void GalleryResponsiveLayout::UpdateMinSize() { SetPreferredMinSize(Size(itemCount * itemWidth + sizeOffset.x, sizeOffset.y)); } GalleryResponsiveLayout::GalleryResponsiveLayout() { SetDirection(ResponsiveDirection::Horizontal); } GalleryResponsiveLayout::~GalleryResponsiveLayout() { } vint GalleryResponsiveLayout::GetMinCount() { return minCount; } vint GalleryResponsiveLayout::GetMaxCount() { return maxCount; } vint GalleryResponsiveLayout::GetItemWidth() { return itemWidth; } Size GalleryResponsiveLayout::GetSizeOffset() { return sizeOffset; } vint GalleryResponsiveLayout::GetVisibleItemCount() { return itemCount; } void GalleryResponsiveLayout::SetMinCount(vint value) { vint oldCount = GetLevelCount(); vint oldLevel = GetCurrentLevel(); if (minCount != value) { if (value < 0) value = 0; minCount = value; if (maxCount < minCount) maxCount = minCount; if (itemCount < minCount) itemCount = minCount; UpdateMinSize(); } bool countChanged = oldCount != GetLevelCount(); bool levelChanged = oldLevel != GetCurrentLevel(); if (countChanged) LevelCountChanged.Execute(GuiEventArgs(this)); if (levelChanged) CurrentLevelChanged.Execute(GuiEventArgs(this)); if (countChanged || levelChanged) OnResponsiveChildLevelUpdated(); } void GalleryResponsiveLayout::SetMaxCount(vint value) { vint oldCount = GetLevelCount(); vint oldLevel = GetCurrentLevel(); if (maxCount != value) { if (value < 0) value = 0; maxCount = value; if (minCount > maxCount) minCount = maxCount; if (itemCount > maxCount) itemCount = maxCount; UpdateMinSize(); } if (oldCount != GetLevelCount()) LevelCountChanged.Execute(GuiEventArgs(this)); if (oldLevel != GetCurrentLevel()) CurrentLevelChanged.Execute(GuiEventArgs(this)); } void GalleryResponsiveLayout::SetItemWidth(vint value) { if (itemWidth != value) { itemWidth = value; UpdateMinSize(); } } void GalleryResponsiveLayout::SetSizeOffset(Size value) { if (sizeOffset != value) { sizeOffset = value; UpdateMinSize(); } } vint GalleryResponsiveLayout::GetLevelCount() { return maxCount - minCount + 1; } vint GalleryResponsiveLayout::GetCurrentLevel() { return itemCount - minCount; } bool GalleryResponsiveLayout::LevelDown() { if (itemCount > minCount) { itemCount--; UpdateMinSize(); CurrentLevelChanged.Execute(GuiEventArgs(this)); return true; } return false; } bool GalleryResponsiveLayout::LevelUp() { if (itemCount < maxCount) { itemCount++; UpdateMinSize(); CurrentLevelChanged.Execute(GuiEventArgs(this)); return true; } return false; } } } } } /*********************************************************************** .\CONTROLS\TOOLSTRIPPACKAGE\GUITOOLSTRIPCOMMAND.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace controls { using namespace collections; using namespace compositions; using namespace regex; using namespace parsing; /*********************************************************************** GuiToolstripCommand ***********************************************************************/ void GuiToolstripCommand::OnShortcutKeyItemExecuted(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { Executed.ExecuteWithNewSender(arguments, sender); } void GuiToolstripCommand::OnRenderTargetChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments) { UpdateShortcutOwner(); } void GuiToolstripCommand::InvokeDescriptionChanged() { GuiEventArgs arguments; DescriptionChanged.Execute(arguments); } void GuiToolstripCommand::ReplaceShortcut(compositions::IGuiShortcutKeyItem* value, Ptr builder) { if (shortcutKeyItem != value) { if (shortcutKeyItem) { shortcutKeyItem->Executed.Detach(shortcutKeyItemExecutedHandler); if (shortcutBuilder) { auto manager = dynamic_cast(shortcutOwner->GetShortcutKeyManager()); if (manager) { manager->DestroyShortcut(shortcutBuilder->ctrl, shortcutBuilder->shift, shortcutBuilder->alt, shortcutBuilder->key); } } } shortcutKeyItem = nullptr; shortcutKeyItemExecutedHandler = nullptr; shortcutBuilder = value ? builder : nullptr; if (value) { shortcutKeyItem = value; shortcutKeyItemExecutedHandler = shortcutKeyItem->Executed.AttachMethod(this, &GuiToolstripCommand::OnShortcutKeyItemExecuted); } InvokeDescriptionChanged(); } } void GuiToolstripCommand::BuildShortcut(const WString& builderText) { List> errors; if (auto parser = GetParserManager()->GetParser(L"SHORTCUT")) { if (Ptr builder = parser->ParseInternal(builderText, errors)) { if (shortcutOwner) { if (!shortcutOwner->GetShortcutKeyManager()) { shortcutOwner->SetShortcutKeyManager(new GuiShortcutKeyManager); } if (auto manager = dynamic_cast(shortcutOwner->GetShortcutKeyManager())) { IGuiShortcutKeyItem* item = manager->TryGetShortcut(builder->ctrl, builder->shift, builder->alt, builder->key); if (!item) { item = manager->CreateShortcut(builder->ctrl, builder->shift, builder->alt, builder->key); if (item) { ReplaceShortcut(item, builder); } } } } else { shortcutBuilder = builder; } } } } void GuiToolstripCommand::UpdateShortcutOwner() { GuiControlHost* host = nullptr; if (auto control = dynamic_cast(attachedRootObject)) { host = control->GetRelatedControlHost(); } else if (auto composition = dynamic_cast(attachedRootObject)) { host = composition->GetRelatedControlHost(); } if (shortcutOwner != host) { if (shortcutOwner) { ReplaceShortcut(nullptr, nullptr); shortcutOwner = nullptr; } shortcutOwner = host; if (shortcutBuilder && !shortcutKeyItem) { BuildShortcut(shortcutBuilder->text); } } } GuiToolstripCommand::GuiToolstripCommand() { } GuiToolstripCommand::~GuiToolstripCommand() { } void GuiToolstripCommand::Attach(GuiInstanceRootObject* rootObject) { GuiGraphicsComposition* rootComposition = nullptr; if (attachedRootObject != rootObject) { if (attachedRootObject) { if (auto control = dynamic_cast(attachedRootObject)) { control->RenderTargetChanged.Detach(renderTargetChangedHandler); } else if (auto composition = dynamic_cast(attachedRootObject)) { composition->GetEventReceiver()->renderTargetChanged.Detach(renderTargetChangedHandler); } renderTargetChangedHandler = nullptr; } attachedRootObject = rootObject; if (attachedRootObject) { if (auto control = dynamic_cast(attachedRootObject)) { renderTargetChangedHandler = control->RenderTargetChanged.AttachMethod(this, &GuiToolstripCommand::OnRenderTargetChanged); } else if (auto composition = dynamic_cast(attachedRootObject)) { renderTargetChangedHandler = composition->GetEventReceiver()->renderTargetChanged.AttachMethod(this, &GuiToolstripCommand::OnRenderTargetChanged); } } UpdateShortcutOwner(); } } void GuiToolstripCommand::Detach(GuiInstanceRootObject* rootObject) { Attach(nullptr); } Ptr GuiToolstripCommand::GetLargeImage() { return largeImage; } void GuiToolstripCommand::SetLargeImage(Ptr value) { if (largeImage != value) { largeImage = value; InvokeDescriptionChanged(); } } Ptr GuiToolstripCommand::GetImage() { return image; } void GuiToolstripCommand::SetImage(Ptr value) { if(image!=value) { image=value; InvokeDescriptionChanged(); } } const WString& GuiToolstripCommand::GetText() { return text; } void GuiToolstripCommand::SetText(const WString& value) { if(text!=value) { text=value; InvokeDescriptionChanged(); } } compositions::IGuiShortcutKeyItem* GuiToolstripCommand::GetShortcut() { return shortcutKeyItem; } void GuiToolstripCommand::SetShortcut(compositions::IGuiShortcutKeyItem* value) { ReplaceShortcut(value, 0); } WString GuiToolstripCommand::GetShortcutBuilder() { return shortcutBuilder ? shortcutBuilder->text : L""; } void GuiToolstripCommand::SetShortcutBuilder(const WString& value) { BuildShortcut(value); } bool GuiToolstripCommand::GetEnabled() { return enabled; } void GuiToolstripCommand::SetEnabled(bool value) { if(enabled!=value) { enabled=value; InvokeDescriptionChanged(); } } bool GuiToolstripCommand::GetSelected() { return selected; } void GuiToolstripCommand::SetSelected(bool value) { if(selected!=value) { selected=value; InvokeDescriptionChanged(); } } /*********************************************************************** GuiToolstripCommand::ShortcutBuilder Parser ***********************************************************************/ class GuiToolstripCommandShortcutParser : public Object, public IGuiParser { typedef GuiToolstripCommand::ShortcutBuilder ShortcutBuilder; public: Regex regexShortcut; GuiToolstripCommandShortcutParser() :regexShortcut(L"((Ctrl)/+|(Shift)/+|(Alt)/+)*(/.+)") { } Ptr ParseInternal(const WString& text, collections::List>& errors)override { Ptr match=regexShortcut.MatchHead(text); if (match && match->Result().Length() != text.Length()) { errors.Add(new ParsingError(L"Failed to parse a shortcut \"" + text + L"\".")); return 0; } Ptr builder = new ShortcutBuilder; builder->text = text; builder->ctrl = match->Groups().Contains(L"ctrl"); builder->shift = match->Groups().Contains(L"shift"); builder->alt = match->Groups().Contains(L"alt"); WString name = match->Groups()[L"key"][0].Value(); builder->key = GetCurrentController()->InputService()->GetKey(name); return builder->key == -1 ? nullptr : builder; } }; /*********************************************************************** GuiToolstripCommandPlugin ***********************************************************************/ class GuiToolstripCommandPlugin : public Object, public IGuiPlugin { public: GUI_PLUGIN_NAME(GacUI_Compiler_ShortcutParser) { GUI_PLUGIN_DEPEND(GacUI_Parser); } void Load()override { IGuiParserManager* manager=GetParserManager(); manager->SetParser(L"SHORTCUT", new GuiToolstripCommandShortcutParser); } void Unload()override { } }; GUI_REGISTER_PLUGIN(GuiToolstripCommandPlugin) } } } /*********************************************************************** .\RESOURCES\GUIDOCUMENT_LOAD.CPP ***********************************************************************/ namespace vl { namespace presentation { using namespace collections; using namespace parsing::tabling; using namespace parsing::xml; using namespace regex; /*********************************************************************** document_operation_visitors::DeserializeNodeVisitor ***********************************************************************/ namespace document_operation_visitors { class DeserializeNodeVisitor : public XmlNode::IVisitor { public: Ptr model; Ptr container; vint paragraphIndex; Ptr resource; Ptr resolver; Regex regexAttributeApply; GuiResourceError::List& errors; DeserializeNodeVisitor(Ptr _model, Ptr _paragraph, vint _paragraphIndex, Ptr _resource, Ptr _resolver, GuiResourceError::List& _errors) :model(_model) , container(_paragraph) , paragraphIndex(_paragraphIndex) , resource(_resource) , resolver(_resolver) , regexAttributeApply(L"/{@([^{}]+)/}") , errors(_errors) { } void PrintText(const WString& text) { Ptr run = new DocumentTextRun; run->text = text; container->runs.Add(run); } void Visit(XmlText* node)override { PrintText(node->content.value); } void Visit(XmlCData* node)override { PrintText(node->content.value); } void Visit(XmlAttribute* node)override { } void Visit(XmlComment* node)override { } void Visit(XmlElement* node)override { Ptr createdContainer; bool useTemplateInfo = false; XmlElement* subNodeContainer = node; if (node->name.value == L"br") { PrintText(L"\r\n"); } else if (node->name.value == L"sp") { PrintText(L" "); } else if (node->name.value == L"tab") { PrintText(L"\t"); } else if (node->name.value == L"img") { Ptr run = new DocumentImageRun; run->baseline = -1; if (Ptr source = XmlGetAttribute(node, L"source")) { run->source = source->value.value; WString protocol, path; if (IsResourceUrl(run->source, protocol, path)) { Ptr imageData = resolver->ResolveResource(protocol, path).Cast(); if (imageData) { run->image = imageData->GetImage(); } if (run->image && run->image->GetFrameCount() > 0) { run->size = run->image->GetFrame(0)->GetSize(); run->frameIndex = 0; } } FOREACH(Ptr, att, node->attributes) { if (att->name.value == L"width") { run->size.x = wtoi(att->value.value); } else if (att->name.value == L"height") { run->size.y = wtoi(att->value.value); } else if (att->name.value == L"baseline") { run->baseline = wtoi(att->value.value); } else if (att->name.value == L"frameIndex") { run->frameIndex = wtoi(att->value.value); } else if (att->name.value != L"source") { errors.Add(GuiResourceError({ {resource},att->name.codeRange.start }, L"Unknown attribute in : \"" + att->name.value + L"\".")); } } container->runs.Add(run); } else { errors.Add(GuiResourceError({ {resource},node->codeRange.start }, L"Attribute \"source\" is missing in .")); } } else if (node->name.value == L"object") { Ptr run = new DocumentEmbeddedObjectRun; run->baseline = -1; if (auto name = XmlGetAttribute(node, L"name")) { run->name = name->value.value; container->runs.Add(run); } else { errors.Add(GuiResourceError({ {resource},node->codeRange.start }, L"The \"name\" attribute in is missing.")); } } else if (node->name.value == L"font") { Ptr run = new DocumentStylePropertiesRun(); Ptr sp = new DocumentStyleProperties; run->style = sp; FOREACH(Ptr, att, node->attributes) { if (att->name.value == L"face") { sp->face = att->value.value; } else if (att->name.value == L"size") { sp->size = DocumentFontSize::Parse(att->value.value); } else if (att->name.value == L"color") { sp->color = Color::Parse(att->value.value); } else if (att->name.value == L"bkcolor") { sp->backgroundColor = Color::Parse(att->value.value); } else { errors.Add(GuiResourceError({ {resource},att->name.codeRange.start }, L"Unknown attribute in : \"" + att->name.value + L"\".")); } } container->runs.Add(run); createdContainer = run; } else if (node->name.value == L"b" || node->name.value == L"b-") { Ptr run = new DocumentStylePropertiesRun(); run->style = new DocumentStyleProperties; run->style->bold = node->name.value == L"b"; container->runs.Add(run); createdContainer = run; } else if (node->name.value == L"i" || node->name.value == L"i-") { Ptr run = new DocumentStylePropertiesRun(); run->style = new DocumentStyleProperties; run->style->italic = node->name.value == L"i"; container->runs.Add(run); createdContainer = run; } else if (node->name.value == L"u" || node->name.value == L"u-") { Ptr run = new DocumentStylePropertiesRun(); run->style = new DocumentStyleProperties; run->style->underline = node->name.value == L"u"; container->runs.Add(run); createdContainer = run; } else if (node->name.value == L"s" || node->name.value == L"s-") { Ptr run = new DocumentStylePropertiesRun(); run->style = new DocumentStyleProperties; run->style->strikeline = node->name.value == L"s"; container->runs.Add(run); createdContainer = run; } else if (node->name.value == L"ha") { Ptr run = new DocumentStylePropertiesRun(); run->style = new DocumentStyleProperties; run->style->antialias = true; run->style->verticalAntialias = false; container->runs.Add(run); createdContainer = run; } else if (node->name.value == L"va") { Ptr run = new DocumentStylePropertiesRun(); run->style = new DocumentStyleProperties; run->style->antialias = true; run->style->verticalAntialias = true; container->runs.Add(run); createdContainer = run; } else if (node->name.value == L"na") { Ptr run = new DocumentStylePropertiesRun(); run->style = new DocumentStyleProperties; run->style->antialias = false; run->style->verticalAntialias = false; container->runs.Add(run); createdContainer = run; } else if (node->name.value == L"div") { if (Ptr att = XmlGetAttribute(node, L"style")) { WString styleName = att->value.value; Ptr run = new DocumentStyleApplicationRun; run->styleName = styleName; container->runs.Add(run); createdContainer = run; } else { createdContainer = container; } } else if (node->name.value == L"a") { Ptr run = new DocumentHyperlinkRun; run->normalStyleName = L"#NormalLink"; run->activeStyleName = L"#ActiveLink"; if (Ptr att = XmlGetAttribute(node, L"normal")) { run->normalStyleName = att->value.value; } if (Ptr att = XmlGetAttribute(node, L"active")) { run->activeStyleName = att->value.value; } if (Ptr att = XmlGetAttribute(node, L"href")) { run->reference = att->value.value; } run->styleName = run->normalStyleName; container->runs.Add(run); createdContainer = run; } else if (node->name.value == L"p") { FOREACH(Ptr, sub, node->subNodes) { sub->Accept(this); } } else { if (node->name.value != L"nop") { errors.Add(GuiResourceError({ {resource},node->codeRange.start }, L"Unknown element in

: \"" + node->name.value + L"\".")); } FOREACH(Ptr, sub, node->subNodes) { sub->Accept(this); } } if (createdContainer) { Ptr oldContainer = container; container = createdContainer; FOREACH(Ptr, subNode, subNodeContainer->subNodes) { subNode->Accept(this); } container = oldContainer; } } void Visit(XmlInstruction* node)override { } void Visit(XmlDocument* node)override { } }; Ptr ParseDocumentStyle(Ptr resource, Ptr styleElement, GuiResourceError::List& errors) { Ptr style=new DocumentStyle; if(Ptr parent=XmlGetAttribute(styleElement, L"parent")) { style->parentStyleName=parent->value.value; } Ptr sp=new DocumentStyleProperties; style->styles=sp; FOREACH(Ptr, att, XmlGetElements(styleElement)) { if(att->name.value==L"face") { sp->face=XmlGetValue(att); } else if(att->name.value==L"size") { sp->size=DocumentFontSize::Parse(XmlGetValue(att)); } else if(att->name.value==L"color") { sp->color=Color::Parse(XmlGetValue(att)); } else if(att->name.value==L"bkcolor") { sp->backgroundColor=Color::Parse(XmlGetValue(att)); } else if(att->name.value==L"b") { sp->bold=XmlGetValue(att)==L"true"; } else if(att->name.value==L"i") { sp->italic=XmlGetValue(att)==L"true"; } else if(att->name.value==L"u") { sp->underline=XmlGetValue(att)==L"true"; } else if(att->name.value==L"s") { sp->strikeline=XmlGetValue(att)==L"true"; } else if(att->name.value==L"antialias") { WString value=XmlGetValue(att); if(value==L"horizontal" || value==L"default") { sp->antialias=true; sp->verticalAntialias=false; } else if(value==L"no") { sp->antialias=false; sp->verticalAntialias=false; } else if(value==L"vertical") { sp->antialias=true; sp->verticalAntialias=true; } } else { errors.Add(GuiResourceError({ {resource},att->codeRange.start }, L"Unknown element in