ultimatepp/uppsrc/ide/LayDes/laywin.cpp
2025-08-18 14:35:06 +02:00

436 lines
12 KiB
C++

#include "LayDes.h"
#include <ide/Common/Common.h>
#include <ide/ide.h>
#define LTIMING(x) //TIMING(x)
#define KEYNAMESPACE LayoutKeys
#define KEYGROUPNAME "Layout"
#define KEYFILE <ide/LayDes/laydes.key>
#include <CtrlLib/key_source.h>
using namespace LayoutKeys;
void LayDes::EditBar(Bar& bar)
{
bool islayout = !IsNull(currentlayout);
bool iscursor = islayout && cursor.GetCount();
bar.Add(iscursor, "Cut", CtrlImg::cut(), THISBACK(Cut))
.Key(K_SHIFT_DELETE)
.Key(K_CTRL_X);
bar.Add(iscursor, "Copy", CtrlImg::copy(), THISBACK(Copy))
.Key(K_CTRL_INSERT)
.Key(K_CTRL_C);
bar.Add(islayout, "Paste", CtrlImg::paste(), THISBACK(Paste))
.Key(K_SHIFT_INSERT)
.Key(K_CTRL_V);
bar.Add(iscursor, "Delete", CtrlImg::remove(), THISBACK(Delete))
.Key(K_DELETE);
bar.Add(iscursor, AK_DUPLICATESEL, LayImg::DuplicateItems(), THISBACK(Duplicate));
bar.Add(iscursor, AK_MATRIXDUPLICATE, THISBACK(Matrix));
bar.Add(islayout, "Select all", CtrlImg::select_all(), THISBACK(SelectAll))
.Key(K_CTRL_A);
bar.Separator();
bar.Add(islayout && CurrentLayout().IsUndo(), "Undo", CtrlImg::undo(), THISBACK(Undo))
.Key(K_ALT_BACKSPACE)
.Key(K_CTRL_Z);
bar.Add(islayout && CurrentLayout().IsRedo(), "Redo", CtrlImg::redo(), THISBACK(Redo))
.Key(K_CTRL_Y)
.Key(K_SHIFT|K_ALT_BACKSPACE)
.Key(K_SHIFT_CTRL_Z);
bar.MenuSeparator();
bar.Add(iscursor, AK_HIDESELECTION, THISBACK1(ShowSelection, false))
.Key(K_CTRL_SUBTRACT);
bar.Add(iscursor, AK_SHOWSELECTION, THISBACK1(ShowSelection, true))
.Key(K_CTRL_ADD);
bar.Separator();
bar.Add(islayout, AK_FINDSOURCE, IdeCommonImg::Cpp(), THISBACK(GotoUsing));
bar.Add(islayout, AK_VISGEN, LayImg::Members(), THISBACK(VisGen));
}
void LayDes::MoveBar(Bar& bar)
{
bool iscursor = !IsNull(currentlayout) && cursor.GetCount();
bar.Add(iscursor, AK_MOVEUP, LayImg::MoveUp(), THISBACK(MoveUp));
bar.Add(iscursor, AK_MOVEDOWN, LayImg::MoveDown(), THISBACK(MoveDown));
bar.Add(cursor.GetCount() >= 2, AK_SORTITEMS, LayImg::SortItems(), THISBACK(SortItems));
}
void LayDes::AlignBar(Bar& bar)
{
bool islayout = !IsNull(currentlayout);
bool iscursor = islayout && cursor.GetCount();
bool group = islayout && cursor.GetCount() > 1;
bar.Add(iscursor, AK_HCENTERLAY, LayImg::HorzCenter(), THISBACK1(Align, A_HORZCENTER));
bar.Add(iscursor, AK_VCENTERLAY, LayImg::VertCenter(), THISBACK1(Align, A_VERTCENTER));
bar.Separator();
bar.Add(group, AK_LEFT, LayImg::AlignLeft(), THISBACK1(Align, A_LEFT));
bar.Add(group, AK_HCENTER, LayImg::AlignHCenter(), THISBACK1(Align, A_HCENTER));
bar.Add(group, AK_RIGHT, LayImg::AlignRight(), THISBACK1(Align, A_RIGHT));
bar.Separator();
bar.Add(group, AK_TOP, LayImg::AlignTop(), THISBACK1(Align, A_TOP));
bar.Add(group, AK_VCENTER, LayImg::AlignVCenter(), THISBACK1(Align, A_VCENTER));
bar.Add(group, AK_BOTTOM, LayImg::AlignBottom(), THISBACK1(Align, A_BOTTOM));
bar.Add(cursor.GetCount(), AK_LABELS, LayImg::AlignLbl(), THISBACK1(Align, A_LABEL));
}
void LayDes::SizeBar(Bar& bar)
{
bool islayout = !IsNull(currentlayout);
bool iscursor = islayout && cursor.GetCount();
bool group = islayout && cursor.GetCount() > 1;
bar.Add(group, AK_SAMEWIDTH, LayImg::SameWidth(), THISBACK1(Align, A_SAMEWIDTH));
bar.Add(group, AK_SAMEHEIGHT, LayImg::SameHeight(), THISBACK1(Align, A_SAMEHEIGHT));
bar.Add(group, AK_SAMESIZE, LayImg::SameSize(), THISBACK1(Align, A_SAMESIZE));
bar.Separator();
bar.Add(iscursor, AK_MINIMALWIDTH, LayImg::MinWidth(), THISBACK1(Align, A_MINWIDTH));
bar.Add(iscursor, AK_MINIMALHEIGHT, LayImg::MinHeight(), THISBACK1(Align, A_MINHEIGHT));
}
void LayDes::SpringBar(Bar& bar)
{
bool islayout = !IsNull(currentlayout);
bool iscursor = islayout && cursor.GetCount();
int va = -1;
int ha = -1;
if(!IsNull(currentlayout) && cursor.GetCount()) {
LayoutData& l = CurrentLayout();
Ctrl::LogPos p = l.item[cursor.Top()].pos;
ha = p.x.GetAlign();
va = p.y.GetAlign();
for(int i = 0; i < cursor.GetCount() - 1; i++) {
p = l.item[cursor[i]].pos;
if(p.x.GetAlign() != ha)
ha = -1;
if(p.y.GetAlign() != va)
va = -1;
}
}
bar.Add(iscursor, AK_SPRINGLEFT, LayImg::SpringLeft(),
THISBACK1(SetSprings, MAKELONG(Ctrl::LEFT, -1)))
.Check(ha == Ctrl::LEFT);
bar.Add(iscursor, AK_SPRINGRIGHT, LayImg::SpringRight(),
THISBACK1(SetSprings, MAKELONG(Ctrl::RIGHT, -1)))
.Check(ha == Ctrl::RIGHT);
bar.Add(iscursor, AK_SPRINGHSIZE, LayImg::SpringHSize(),
THISBACK1(SetSprings, MAKELONG(Ctrl::SIZE, -1)))
.Check(ha == Ctrl::SIZE);
bar.Add(iscursor, AK_SPRINGHCENTER, LayImg::SpringHCenter(),
THISBACK1(SetSprings, MAKELONG(Ctrl::CENTER, -1)))
.Check(ha == Ctrl::CENTER);
bar.Separator();
bar.Add(iscursor, AK_SPRINGTOP, LayImg::SpringTop(),
THISBACK1(SetSprings, MAKELONG(-1, Ctrl::TOP)))
.Check(va == Ctrl::TOP);
bar.Add(iscursor, AK_SPRINGBOTTOM, LayImg::SpringBottom(),
THISBACK1(SetSprings, MAKELONG(-1, Ctrl::BOTTOM)))
.Check(va == Ctrl::BOTTOM);
bar.Add(iscursor, AK_SPRINGVSIZE, LayImg::SpringVSize(),
THISBACK1(SetSprings, MAKELONG(-1, Ctrl::SIZE)))
.Check(va == Ctrl::SIZE);
bar.Add(iscursor, AK_SPRINGVCENTER, LayImg::SpringVCenter(),
THISBACK1(SetSprings, MAKELONG(-1, Ctrl::CENTER)))
.Check(va == Ctrl::CENTER);
bar.Separator();
bar.Add(iscursor, AK_AUTOSPRINGS, LayImg::SpringAuto(),
THISBACK1(SetSprings, MAKELONG(AUTOSPRING, 0)));
bar.Add(iscursor, AK_TOPLEFTSPRINGS, LayImg::SpringTopLeft(),
THISBACK1(SetSprings, MAKELONG(Ctrl::LEFT, Ctrl::TOP)));
}
void LayDes::ToggleGrid()
{
usegrid = !usegrid;
SetBar();
}
void LayDes::ToggleMinSize()
{
ignoreminsize = !ignoreminsize;
SetBar();
}
void LayDes::ToggleSizeSpring()
{
sizespring = !sizespring;
SetBar();
}
void LayDes::Settings()
{
setting.Execute();
Refresh();
SyncItems();
}
void LayDes::GotoUsing()
{
if(IsNull(currentlayout))
return;
if(item.IsCursor()) // TODO not for label
TheIde()->FindDesignerItemReferences("With" + CurrentLayout().name + "::" + ~item.Get(1), ~item.Get(1));
else
TheIde()->FindDesignerItemReferences("With" + CurrentLayout().name, CurrentLayout().name);
}
void LayDes::OptionBar(Bar& bar)
{
bar.Add("Zoom " + AsString(GetScale() * 100) + "%", MakeZoomIcon(GetScale()),
[=] {
layout_zoom = layout_zoom < 5 ? 5 : layout_zoom < 10 ? 10 : 0;
Refresh();
SetBar();
SetSb();
});
bar.Add("Use grid", LayImg::Grid(), THISBACK(ToggleGrid))
.Check(usegrid);
bar.Add("Ignore min size", LayImg::MinSize(), THISBACK(ToggleMinSize))
.Check(ignoreminsize);
bar.Add("Resize with springs", LayImg::SizeSpring(), THISBACK(ToggleSizeSpring))
.Check(sizespring);
bar.Add("Settings..", THISBACK(Settings));
}
void LayDes::HelpBar(Bar& bar)
{
bar.Add("Learn more about Layout Designer..", IdeCommonImg::Help(),
[=] { LaunchWebBrowser("https://www.ultimatepp.org/app$ide$LayoutDes_en-us.html"); });
}
void LayDes::MainToolBar(Bar& bar)
{
EditBar(bar);
bar.Separator();
MoveBar(bar);
bar.Separator();
OptionBar(bar);
bar.Separator();
bar.Add(status, INT_MAX);
bar.Break();
AlignBar(bar);
bar.Separator();
SizeBar(bar);
bar.Separator();
SpringBar(bar);
bar.GapRight();
bar.Separator();
HelpBar(bar);
}
void LayDes::ItemBar(Bar& bar)
{
bar.Add("Position", THISBACK(MoveBar));
bar.Add("Alignment", THISBACK(AlignBar));
bar.Add("Size", THISBACK(SizeBar));
bar.Add("Springs", THISBACK(SpringBar));
}
void LayDes::MainMenuBar(Bar& bar)
{
bar.Add("Edit", THISBACK(EditBar));
bar.Add("Item", THISBACK(ItemBar));
bar.Add("Options", THISBACK(OptionBar));
}
void LayDes::EditMenu(Bar& bar)
{
EditBar(bar);
bar.Separator();
bar.Add("Item", THISBACK(ItemBar));
bar.Separator();
OptionBar(bar);
}
void LayDes::SetBar()
{
LTIMING("SetBar");
toolbar.Set(THISBACK(MainToolBar));
}
class CVFrame : public CtrlFrame {
virtual void FrameLayout(Rect& r) { r.bottom -= 4; r.left += 2; r.right -= 2; }
virtual void FramePaint(Draw& w, const Rect& r) { w.DrawRect(r, SColorFace); }
virtual void FrameAddSize(Size& sz) { sz.cy += 4; sz.cx += 4; }
};
void LayDes::Serialize(Stream& s)
{
int version = 2;
s / version;
s % setting.gridx % setting.gridy;
s % setting.paintgrid % setting.showicons;
s % ignoreminsize % usegrid;
if(version >= 1)
s % sizespring;
s % lsplit % isplit % rsplit;
if(version >= 2)
s % layout_zoom;
item.SerializeHeader(s);
SetBar();
}
int TypeFilter(int c)
{
return iscid(c) || c == '<' || c == '>' || c == ':' || c == ',' ? c : 0;
}
int VariableFilter(int c)
{
return iscid(c) || c == '.' || c == '[' || c == ']' ? c : 0;
}
bool LayDes::HotKey(dword key)
{
return MenuBar::Scan(list.WhenBar, key) ||
MenuBar::Scan(item.WhenBar, key) ||
StaticRect::HotKey(key);
}
LayDes::LayDes()
{
charset = CHARSET_UTF8;
currentlayout = Null;
draghandle = -1;
usegrid = true;
ignoreminsize = false;
sizespring = false;
SetBar();
status.Width(400);
status.NoTransparent();
km.d = this;
km.Add(lsplit.SizePos());
lsplit.Horz(rsplit, *this).SetPos(2000);
rsplit.Vert(layouts, isplit);
int cy = EditString::GetStdHeight();
layouts.Add(search.HSizePos().TopPos(0, cy));
layouts.Add(list.HSizePos().VSizePos(cy, 0));
rsplit.SetPos(1000);
isplit.Vert(item, property);
twsplit.Height(EditField::GetStdHeight() + 4);
twsplit.Horz(type, variable);
twsplit.SetFrame(Single<CVFrame>());
property.AddFrame(twsplit);
property.AddFrame(ThinInsetFrame());
property.AfterCreate();
type <<= THISBACK(TypeEdit);
variable <<= THISBACK(VariableEdit);
type.Disable();
variable.Disable();
type.SetFilter(TypeFilter);
variable.SetFilter(VariableFilter);
item.AddColumn("Type", 20).Margin(0);
item.AddColumn("Var / lbl", 10).Margin(0);
item.WhenLeftClick = THISBACK(ItemClick);
item.NoWantFocus();
item.WhenBar = THISBACK(MoveBar);
item.WhenDrag = THISBACK(Drag);
item.WhenDropInsert = THISBACK(DnDInsert);
SetFrame(toolbar);
AddFrame(InsetFrame());
AddFrame(sb);
sb.WhenScroll = THISBACK(Scroll);
list.NoHeader().NoGrid();
list.AddKey();
list.AddColumn();
list.WhenCursor = THISBACK(LayoutCursor);
list.WhenBar = THISBACK(LayoutMenu);
list.WhenLeftDouble = THISBACK(RenameLayout);
list.NoWantFocus();
list.WhenDrag = THISBACK(DragLayout);
list.WhenDropInsert = THISBACK(DnDInsertLayout);
search.NullText("Search (Ctrl+F)");
search <<= THISBACK(Search);
search.SetFilter(CharFilterToUpper);
NoWantFocus();
item.NoWantFocus();
BackPaint();
SyncUsc();
CtrlLayoutOKCancel(matrix, "Matrix duplicate");
matrix.nx.MinMax(1, 32);
matrix.nx <<= 2;
matrix.dx.MinMax(1, 32);
matrix.dx <<= 0;
matrix.ny.MinMax(1, 32);
matrix.ny <<= 1;
matrix.dy.MinMax(1, 32);
matrix.dy <<= 0;
CtrlLayoutOKCancel(setting, "Settings");
setting.gridx.MinMax(1, 32);
setting.gridx <<= 4;
setting.gridy.MinMax(1, 32);
setting.gridy <<= 4;
}
LayDesigner *CreateLayDesigner(const char *filename, byte charset, const char *cfgname)
{
LayDesigner *q = new LayDesigner();
LoadFromGlobal(*q, "laydes-ctrl");
if(q->Load(filename, charset))
return q;
delete q;
return NULL;
}
void LayUscClean();
bool LayUscParse(CParser& p, String& current_namespace);
void SerializeLayEditPos(Stream& s);
bool IsLayFile(const char *path)
{
return ToLower(GetFileExt(path)) == ".lay";
}
struct LayDesModule : public IdeModule {
virtual String GetID() { return "LayDesModule"; }
virtual void CleanUsc() {
LayUscClean();
}
virtual bool ParseUsc(CParser& p, String& current_namespace) {
return LayUscParse(p, current_namespace);
}
virtual Image FileIcon(const char *path) {
return IsLayFile(path) ? LayImg::Layout() : Null;
}
virtual IdeDesigner *CreateDesigner(const char *path, byte cs) {
if(IsLayFile(path)) {
LayDesigner *d = CreateLayDesigner(path, cs, "laydes-ctrl");
return d;
}
return NULL;
}
virtual void Serialize(Stream& s) {
int version = 0;
s / version;
SerializeLayEditPos(s);
}
virtual ~LayDesModule() {}
};
void RegisterLayDes()
{
RegisterIdeModule(Single<LayDesModule>());
}
INITBLOCK {
RegisterGlobalConfig("laydes-ctrl");
}