-
Posts
929 -
Joined
-
Last visited
Content Type
Blogs
Forums
Store
Gallery
Videos
Downloads
Everything posted by klepto2
-
static const and scoped enums are 2 different things. the current enums look like a mix of enums names for constants. Normally only constants are uppercase. The thing is you don't need the uppercase-separated-by-underscore approach. You can keep the uppercase names of course: CreateTextfield(0,0,250,20,ui->root, TextfieldStyle::DEFAULT); // you can now clearly say what enum is used. with your approach you can't have something like this: enum VerticalAlignment { TOP,BOTTOM,CENTER }; enum HorizontalAlignment { LEFT,RIGHT,CENTER }; which leads to the need of the underscore approach. with scoped enums this is legal: enum class VerticalAlignment { TOP,BOTTOM,CENTER }; enum class HorizontalAlignment { LEFT,RIGHT,CENTER }; as mentioned in the above post in my opinion it feels cleaner and is more collision safe with other libraries.
-
Currently the enums in UAK are defined as unscoped enums like this: enum TextFieldStyle { TEXTFIELD_READONLY = 1, //TEXTFIELD_PASSWORD = 2, TEXTFIELD_TEXTCHANGEACTIONEVENT = 4, TEXTFIELD_LOSEFOCUSACTIONEVENT = 8, TEXTFIELD_ENTERKEYACTIONEVENT = 16, TEXTFIELD_DEFAULT = TEXTFIELD_LOSEFOCUSACTIONEVENT + TEXTFIELD_ENTERKEYACTIONEVENT, //TEXTFIELD_NUMBER = 8, //TEXTFIELD_INTEGER = 16, //TEXTFIELD_FILEPATH = 32, TEXTFIELD_PASSWORD = 64 }; I suggest to follow the common c++ suggestions to use scoped enums instead: enum class TextFieldStyle { ReadOnly = 1, Password = 2, TextChangeActionEvent = 4, LoseFocusActionEvent = 8, EnterKeyActionEvent = 16, Default = LoseFocusActionEvent + EnterKeyActionEvent, }; The scoped enums have much more possibilities: 1. You can have the same enum-value name across multiple enums (eg Default or Left) 2. enums can have a different value type instead of just int 3. the code completion is much more intuitive in most IDEs. 4. the code is more readable (in my opinion)
-
Forgot to mention: OS: Windows 10.
-
-
class TooltipInfo : public Object { public: shared_ptr<Widget> target; shared_ptr<Widget> tooltipWidget; }; enum class TooltipState { Pending, Show, Showing, Hiding, Hide, }; class TooltipService { static vector<shared_ptr<TooltipInfo>> _tooltipInfos; static bool _initialized; static TooltipState _state; static shared_ptr<Window> _window; static shared_ptr<Interface> _ui; static bool Enter(const Event&, shared_ptr<Object>); static bool Leave(const Event&, shared_ptr<Object>); static bool Tick(const Event&, shared_ptr<Object>); static void Init(); static shared_ptr<Timer> _timer; static shared_ptr<TooltipInfo> _activeWidget; static int _timerstart; static void ShowTooltip(shared_ptr<TooltipInfo> widget); static void HideTooltip(); public: static void RegisterTooltip(shared_ptr<Widget> target, shared_ptr<Widget> tooltipControl = nullptr); }; vector<shared_ptr<TooltipInfo>> TooltipService::_tooltipInfos = vector<shared_ptr<TooltipInfo>>(); bool TooltipService::_initialized = false; shared_ptr<TooltipInfo> TooltipService::_activeWidget = nullptr; shared_ptr<Timer> TooltipService::_timer = nullptr; int TooltipService::_timerstart = -1; TooltipState TooltipService::_state = TooltipState::Pending; shared_ptr<Window> TooltipService::_window = nullptr; shared_ptr<Interface> TooltipService::_ui = nullptr; bool TooltipService::Tick(const Event& ev, shared_ptr<Object> obj) { if (_state == TooltipState::Show && (Millisecs() - _timerstart) > 500) { ShowTooltip(_activeWidget); } else if (_state == TooltipState::Hiding && (Millisecs() - _timerstart) > 200) { _state = TooltipState::Hide; } else if (_state == TooltipState::Hide) { HideTooltip(); } return false; } void TooltipService::ShowTooltip(shared_ptr<TooltipInfo> widget) { if (_state == TooltipState::Show) { HideTooltip(); } if (widget->tooltipWidget == nullptr) { return; } auto mainwindow = widget->target->gui->GetWindow(); auto bordersize = mainwindow->GetBorder(); auto mp = mainwindow->GetMousePosition(); auto wp = mainwindow->GetPosition(); _window->SetShape(mp.x + wp.x +15, mp.y + wp.y + 50, widget->tooltipWidget->size.x, widget->tooltipWidget->size.y); widget->tooltipWidget->Show(); _window->Show(); _state = TooltipState::Showing; } void TooltipService::HideTooltip() { _window->Hide(); _state = TooltipState::Pending; } bool TooltipService::Enter(const Event& ev, shared_ptr<Object> obj) { _activeWidget = obj->As<TooltipInfo>(); _timerstart = Millisecs(); _state = TooltipState::Show; return false; } bool TooltipService::Leave(const Event& ev, shared_ptr<Object> obj) { _activeWidget = nullptr; _state = TooltipState::Hiding; _timerstart = Millisecs(); return false; } void TooltipService::Init() { auto displays = GetDisplays(); _window = CreateWindow("", 0, 0, 0, 0, displays[0], WindowStyles::WINDOW_HIDDEN | WindowStyles::WINDOW_CHILD); SetWindowLongA(_window->GetHandle(), GWL_EXSTYLE, (GetWindowLong(_window->GetHandle(), GWL_EXSTYLE) | WS_EX_TOOLWINDOW) & ~WS_EX_APPWINDOW); _ui = CreateInterface(_window); _timer = CreateTimer(50); ListenEvent(EventID::EVENT_TIMERTICK, _timer, TooltipService::Tick); _initialized = true; } void TooltipService::RegisterTooltip(shared_ptr<Widget> target, shared_ptr<Widget> tooltipControl) { if (!_initialized) { Init(); } auto info = make_shared<TooltipInfo>(); info->target = target; info->tooltipWidget = tooltipControl; if (tooltipControl != nullptr) { tooltipControl->Hide(); tooltipControl->SetParent(_ui->root); } _tooltipInfos.push_back(info); ListenEvent(EventID::EVENT_MOUSEENTER, target, TooltipService::Enter, info); ListenEvent(EventID::EVENT_MOUSELEAVE, target, TooltipService::Leave, info); } Very simple tooltip system (just a small prototype) (win32 only, for other remove the SetWindowLongA part in the init method). This is how to use it: auto tooltipControl = CreatePanel(0, 0, 250, 80, ui->root, PANEL_BORDER); tooltipControl->SetColor(Vec4(0.7,0.7,0.7, 1.0), WidgetColor::WIDGETCOLOR_BACKGROUND); auto tooltipLabel = CreateLabel("Some tooltip.", 0, 0, tooltipControl->size.x, tooltipControl->size.y, tooltipControl, LabelStyle::LABEL_CENTER | LabelStyle::LABEL_MIDDLE); tooltipLabel->SetLayout(1, 1, 1, 1); TooltipService::RegisterTooltip(toolbarbutton_open, tooltipControl); TooltipService::RegisterTooltip(toolbarbutton_save); TooltipService::RegisterTooltip(toolbarbutton_options); TooltipService::RegisterTooltip(toolbarbutton_help);
-
Hi! I was on vacation the last 2 weeks so i couldn't reply earlier. Take a look here: I am currently working on a full featured layout system with Container Gadgets which will work like (kind of) the way you can layout things in xaml or in other GUI libraries. I will post more info on that new system in a later blog entry but in general it should work the way you described. There will be Container, Grid, Flow, Stackpanel and other widgets to control the layout as well as additional layouting features (Alignment, Margin, Padding, etc.)
-
Made some progress with the ScintillaWidget: Scrollbars are native SliderWidgets from UAK Also i have the Notifcationsystem aka Events from Scintilla integrated to UAK.
- 2 comments
-
- 1
-
In this first entry I will introduce some basics about the internal structure of the Widget class and what the purpose of some of its members is. For the real basics on how to write a custom Widget I suggest reading this https://www.ultraengine.com/learn/CPP/CustomWidgets first. Most of the members are self explained, so I will focus in this entry on 4 important methods: virtual bool Initialize(const WString& text, const int x, const int y, const int width, const int height, shared_ptr<Widget> parent, const int style); virtual void UpdateLayout(); virtual void Draw(const int x, const int y, const int width, const int height); virtual void Draw__(const int x, const int y, const int width, const int height); The "Initialize" method: As the name says, this method initialize the Widget. In this method you can define additional parameters, the block size needed for the drawing etc. This method should always be used in the custom Create-Function and unless really needed it should not be overridden or at least the base method should be called like this: bool CustomWidget::Initialize(const WString& text, const int x, const int y, const int width, const int height, shared_ptr<Widget> parent, const int style) { if (Widget::Initialize(text, x, y, width, height, parent, style)) { //your initialisation goes here } } Internally, the "Initialize" method setups the initial size, the parent, style and text. The "UpdateLayout" method: This method is called everytime the position or the size of the widget changes and can be overridden to support custom layouting. When overridden keep in mind that this method is dependend on the "SetLayout" method, so you should either call the base method to support the current layout settings and update the Clientsizes or you need to calculate it by yourself. In this method you can calculate or maintain a list of visible children which can later be used in the Draw__ method. The "Draw" method: This method is normally the most common one to override as this method defines how the widget is shown on screen and is called everytime the widget is redrawn. The drawing in the UltraAppKit is defined by Blocks, these blocks are drawn in order and for speed you should consider to update the block structure when the layout changes or events need a visual feedback: virtual void MouseEnter(const int x, const int y) { hover = true; Redraw(); } //Called each time the widget is redrawn virtual void Draw(const int x, const int y, const int width, const int height) { blocks.clear(); Vec4 color = Vec4(1, 0, 0, 1); if (hover) color = Vec4(0, 1, 0, 1); //Background rectangle AddBlock(iVec2(0), this->size, color); //Foreground text AddBlock(text, iVec2(0), this->size, Vec4(1), TEXT_CENTER | TEXT_MIDDLE); } I will explain the "Draw"-Method with the "Blocks"-Structure in depth in a later entry. The "Draw__" method: This method is not documented and may be subject to change, but this method is very important in case you want to write something which needs more in depth handling over the drawing itself. Like the "Draw"-Method this is called as well when the widget needs to be redrawn. From my observations this is the internal main entry point to the Redraw-Chain it is important to at least call the own "Draw"-Method here. This method also needs to call the Draw__ of each of its kids so that these are drawn as well. In this method you can limit the children which are drawn on screen when you maintain a visibility list in the UpdateLayout method. More on this in a later entry. What comes next: This is just a short overview from my own observations and some info might be incorrect or change in the future. The following entries will explain more in depth details about writing a custom widget with some hints and tips I came across I am currently developing a small library of custom widgets myself which will include at least the following: Layout: FlowLayoutPanel (ready) StackPanel (ready) GridLayoutPanel (in progress) Other: ContentPanel (a control host with automatic content scrolling) TextEditor (based on Scintilla, in Progress) and more (you can suggest widgets you might need, and I will see what I can do) here is a small code sample how the GridLayoutPanel (and a preview of the TextEditor) works and how it looks: auto layout = CreateGridLayoutPanel(0, mainmenu->size.y, sz.x, sz.y- mainmenu->size.y, ui->root); layout->SetLayout(1, 1, 1, 1); layout->AddRowDefinition(GridLength::PIXEL, TOOLBARHEIGHT); layout->AddRowDefinition(GridLength::STAR); layout->AddRowDefinition(GridLength::PIXEL, CONSOLEHEIGHT - 28); layout->AddRowDefinition(GridLength::PIXEL, 28); layout->AddRowDefinition(GridLength::PIXEL, STATUSBARHEIGHT); layout->AddColumnDefinition(GridLength::STAR,3); layout->AddColumnDefinition(GridLength::STAR,1); auto toolbar = CreatePanel(0, 0, 0, 0, layout); toolbar->SetColor(1, 1, 0); auto statusbar = CreatePanel(0, 0,0,0, layout); statusbar->SetColor(0, 1, 0); auto mainpanel = CreatePanel(0, 0, 0, 0, layout); mainpanel->SetColor(0, 0, 1); auto sidepanel = CreatePanel(0, 0, 0, 0, layout); sidepanel->SetColor(1, 0, 0); auto consolepanel = CreatePanel(0, 0, 0, 0, layout); consolepanel->SetColor(0.5, 1, 0); auto inputpanel = CreatePanel(0, 0, 0, 0, layout); inputpanel->SetColor(0.3, 1, 0.5); layout->AssignChild(toolbar, 0, 0, 2); //Assign toolbar to grid col 0 and row 0 with a columnspan of 2 layout->AssignChild(statusbar, 0, 4, 2);//Assign statusbar to grid col 0 and row 4 with a columnspan of 2 layout->AssignChild(mainpanel, 0, 1);//Assign mainpanel to grid col 0 and row 1 layout->AssignChild(sidepanel, 1, 1, 1, 3); //Assign sidepanel to grid col 1 and row 1 with a rowspan of 2 layout->AssignChild(consolepanel, 0, 2);//Assign consolepanel to grid col 0 and row 2 layout->AssignChild(inputpanel, 0, 3);//Assign inputpanel to grid col 0 and row 3 layout->UpdateLayout(); auto mpcs = mainpanel->ClientSize(); auto editor = CreateTextEditor(0, 0, mpcs.x, mpcs.y, mainpanel); editor->SetLayout(1, 1, 1, 1); auto textWidth = editor->TextWidth(STYLE_LINENUMBER, "_99999"); editor->SetMargins(5); editor->SetMarginTypeN(0,MarginType::Number); editor->SetMarginWidthN(0,textWidth); editor->SetMarginTypeN( 2, MarginType::Symbol); editor->SetMarginMaskN( 2, SC_MASK_FOLDERS); editor->SetMarginWidthN( 2, 20); editor->MarkerDefine( SC_MARKNUM_FOLDER,MarkerSymbol::BoxPlusConnected); editor->MarkerDefine( SC_MARKNUM_FOLDEROPEN, MarkerSymbol::BoxMinusConnected); editor->MarkerDefine( SC_MARKNUM_FOLDEREND, MarkerSymbol::BoxPlus); editor->MarkerDefine( SC_MARKNUM_FOLDERMIDTAIL, MarkerSymbol::TCorner); editor->MarkerDefine( SC_MARKNUM_FOLDEROPENMID, MarkerSymbol::BoxMinusConnected); editor->MarkerDefine( SC_MARKNUM_FOLDERSUB, MarkerSymbol::VLine); editor->MarkerSetBack(SC_MARKNUM_FOLDERSUB, RGB(128,128,128)); editor->MarkerDefine( SC_MARKNUM_FOLDERTAIL, MarkerSymbol::LCorner); editor->MarkerSetBack(SC_MARKNUM_FOLDERMIDTAIL, RGB(128, 128, 128)); editor->MarkerSetBack(SC_MARKNUM_FOLDERSUB, RGB(128, 128, 128)); editor->MarkerSetBack(SC_MARKNUM_FOLDERTAIL, RGB(128, 128, 128)); editor->SetFoldFlags(FoldFlag::LineAfterContracted); // 16 Draw line below if not expanded auto lualexer = CreateLexer("lua"); editor->SetILexer(lualexer); editor->StyleSetFore(SCE_LUA_WORD, RGB(0,0,255)); editor->StyleSetFore(SCE_LUA_WORD2, RGB(0, 128, 128)); editor->StyleSetFore(SCE_LUA_COMMENT, RGB(0, 255, 0)); editor->StyleSetFore(SCE_LUA_COMMENTLINE, RGB(0, 255, 0)); editor->StyleSetFore(SCE_LUA_COMMENTDOC, RGB(0, 255, 0)); editor->StyleSetFore(SCE_LUA_CHARACTER, RGB(255, 0, 0)); editor->StyleSetFore(SCE_LUA_STRING, RGB(255, 0, 0)); editor->SetKeyWords( 0, "and break do else elseif end for function if in local nil not or repeat return then until while"); editor->SetKeyWords(1, "print require"); editor->SetProperty( "fold", "1"); editor->SetAutomaticFold(AutomaticFold::Change | AutomaticFold::Click | AutomaticFold::Show); editor->SetMarginSensitiveN( 2, 1);
- 2 comments
-
- 6
-
Slider not scrolling with mousedrag when window is too small
klepto2 replied to klepto2's topic in Bug Reports
No problem ; ) i just thought the description needed some addition to make the problem more clear. -
Slider not scrolling with mousedrag when window is too small
klepto2 replied to klepto2's topic in Bug Reports
Well, while I agree that too high values might lead to problems I doubt that this is the case here. it only occurs while using the knob dragging with the mouse, setting the value with the mousewheel or directly doesn't have the issue When I set the values to 2 and 8 I lose a precision of 100px in the case i want Pixel Perfect scrolling ( Imageviewer) -
Slider not scrolling with mousedrag when window is too small
klepto2 replied to klepto2's topic in Bug Reports
As a side node, the scrolling behaviour when dragging with a mouse is a bit uncomfortable. It seems to move with the slower than the actualmousespeed. The common behaviour would be that the knob is synchron with the actual mouseposition. -
It seems there is a bug where you can't scroll the slider using mousedragging when the containing window is too small: #include "UltraEngine.h" using namespace UltraEngine; bool RescaleUI(const Event& event, shared_ptr<Object> extra) { float dpiscale = float(event.data) / 100.0f; auto ui = dynamic_pointer_cast<Interface>(extra); ui->SetScale(dpiscale); auto window = dynamic_pointer_cast<Window>(event.source); window->SetShape(event.position.x, event.position.y, event.size.x, event.size.y); return true; } int main(int argc, const char* argv[]) { //Get displays auto displays = GetDisplays(); if (displays.empty()) return 1; float dpiscale = displays[0]->scale; //Create window auto mainwindow = CreateWindow("Ultra App Kit", 0, 0, 300 * dpiscale, 400 * dpiscale, displays[0], WINDOW_HIDDEN | WINDOW_CENTER | WINDOW_TITLEBAR | WINDOW_RESIZABLE); //Create user interface auto ui = CreateInterface(mainwindow); iVec2 sz = ui->root->ClientSize(); auto label = CreateSlider(0, 0, 20, sz.y, ui->root, SLIDER_SCROLLBAR | SLIDER_VERTICAL); label->SetLayout(1, 0, 1, 1); label->SetRange(200,800); //Enable DPI scaling changes ui->SetScale(dpiscale); ListenEvent(EVENT_WINDOWDPICHANGED, mainwindow, RescaleUI, ui); //Show the window mainwindow->Show(); mainwindow->Activate(); while (true) { const Event event = WaitEvent(); switch (event.id) { case EVENT_WIDGETSELECT: break; case EVENT_WIDGETACTION: break; case EVENT_WINDOWCLOSE: if (event.source == mainwindow) return 0; break; } } return 0; } When you start this, you can't scroll the slider, but if you resize the window to some extend the dragging starts working.
-
While I am preparing the different parts of the blog i will show you a small summary about the ocean rendering i am using: GPU-based FFT waves using Phillipsspectrum Foam rendering (shore and whitecaps) Refraction and Reflection (realtime, ssr compatible) Fast due to Projected grid concept Teaser images: