Merge branches 'vapoursynth' and 'info' into feature
This commit is contained in:
commit
9e8ac83998
8 changed files with 67 additions and 24 deletions
13
README.md
13
README.md
|
@ -1,5 +1,7 @@
|
||||||
## arch1t3cht's Aegisub "fork"
|
## arch1t3cht's Aegisub "fork"
|
||||||
Go [here](#branchfeature-list) for the new features.
|
Download release builds [here](https://github.com/arch1t3cht/Aegisub/releases), or the latest CI builds [here](https://github.com/arch1t3cht/Aegisub/actions).
|
||||||
|
|
||||||
|
The release page also has detailed list of all changes and new features. If you're interested in the technical details or want to compile yourself, read on.
|
||||||
|
|
||||||
### Don't we have enough Aegisub forks already??
|
### Don't we have enough Aegisub forks already??
|
||||||
We absolutely do, and I'm aware that adding another one [doesn't sound like](https://xkcd.com/927/) a [good idea on paper](https://cdn.discordapp.com/attachments/425357202963038208/1007103606421459004/unknown.png). However,
|
We absolutely do, and I'm aware that adding another one [doesn't sound like](https://xkcd.com/927/) a [good idea on paper](https://cdn.discordapp.com/attachments/425357202963038208/1007103606421459004/unknown.png). However,
|
||||||
|
@ -50,9 +52,6 @@ This is probably because you're building with wxgtk2. Building with wxgtk3 fixes
|
||||||
|
|
||||||
The exact way of switching depends on your Linux distribution, but essentially you need to ensure that `wx-config` or the next best variant of it points to wxgtk3. If it points to wxgtk2 by default and deinstalling wxgtk2 isn't an option, you can also temporarily move it out of the path or use a `native-file` in your meson project. Then, fully reconfigure meson using `meson configure --clearcache` and `meson setup --reconfigure`.
|
The exact way of switching depends on your Linux distribution, but essentially you need to ensure that `wx-config` or the next best variant of it points to wxgtk3. If it points to wxgtk2 by default and deinstalling wxgtk2 isn't an option, you can also temporarily move it out of the path or use a `native-file` in your meson project. Then, fully reconfigure meson using `meson configure --clearcache` and `meson setup --reconfigure`.
|
||||||
|
|
||||||
#### I get errors like "Option not found" after merging one of these branches
|
|
||||||
The changes to `default_config.json` or similar files weren't detected by meson due to missing regen dependencies. You can either merge the `bugfixes` branch or rebuild from scratch.
|
|
||||||
|
|
||||||
#### The video is desynced / Frames don't appear at the right time
|
#### The video is desynced / Frames don't appear at the right time
|
||||||
This is probably due to the ffms2 seeking bug ([#394](https://github.com/FFMS/ffms2/issues/394)). On Windows, this specific regression shouldn't happen anymore. On Linux, you need to install the latest git version of ffms2 - for example the [`ffms2-git`](https://aur.archlinux.org/packages/ffms2-git) AUR package on Arch linux, or just compile it yourself.
|
This is probably due to the ffms2 seeking bug ([#394](https://github.com/FFMS/ffms2/issues/394)). On Windows, this specific regression shouldn't happen anymore. On Linux, you need to install the latest git version of ffms2 - for example the [`ffms2-git`](https://aur.archlinux.org/packages/ffms2-git) AUR package on Arch linux, or just compile it yourself.
|
||||||
|
|
||||||
|
@ -63,11 +62,13 @@ If you're compiling yourself, try adding `--force-fallback-for=zlib` to the meso
|
||||||
|
|
||||||
|
|
||||||
### Compilation
|
### Compilation
|
||||||
|
If you're just looking to install Aegisub, you might want to check out the [releases page](https://github.com/arch1t3cht/Aegisub/releases) or the [CI builds](https://github.com/arch1t3cht/Aegisub/actions) first.
|
||||||
|
|
||||||
For compilation on Windows, see the TSTools documentation below. Also check the [GitHub workflow](https://github.com/arch1t3cht/Aegisub/blob/cibuilds/.github/workflows/ci.yml) for the project arguments.
|
For compilation on Windows, see the TSTools documentation below. Also check the [GitHub workflow](https://github.com/arch1t3cht/Aegisub/blob/cibuilds/.github/workflows/ci.yml) for the project arguments.
|
||||||
|
|
||||||
On Arch Linux, there is an AUR package called [aegisub-arch1t3cht-git](https://aur.archlinux.org/packages/aegisub-arch1t3cht-git). It's not maintained by me but seems to work.
|
On Arch Linux, there is an AUR package called [aegisub-arch1t3cht-git](https://aur.archlinux.org/packages/aegisub-arch1t3cht-git). It's not maintained by me but seems to work.
|
||||||
|
|
||||||
On other distributions or for manual compilation you can use this package or the [TSTools PKGBUILD](https://aur.archlinux.org/packages/aegisub-ttools-meson-git) as a reference, in particular for installing the necessary dependencies if you don't want to compile them yourself.
|
On other Linux distributions or for manual compilation you can use this package or the [TSTools PKGBUILD](https://aur.archlinux.org/packages/aegisub-ttools-meson-git) as a reference, in particular for installing the necessary dependencies if you don't want to compile them yourself.
|
||||||
If all dependencies are installed:
|
If all dependencies are installed:
|
||||||
- Install Meson
|
- Install Meson
|
||||||
- Clone the repository
|
- Clone the repository
|
||||||
|
@ -132,7 +133,7 @@ All other dependencies are either stored in the repository or are included as su
|
||||||
|
|
||||||
Building:
|
Building:
|
||||||
|
|
||||||
1. Clone Aegisub's repository: `git clone https://github.com/TypesettingTools/Aegisub.git`
|
1. Clone Aegisub's repository: `git clone https://github.com/arch1t3cht/Aegisub.git`
|
||||||
2. From the Visual Studio "x64 Native Tools Command Prompt", generate the build directory: `meson build -Ddefault_library=static` (if building for release, add `--buildtype=release`)
|
2. From the Visual Studio "x64 Native Tools Command Prompt", generate the build directory: `meson build -Ddefault_library=static` (if building for release, add `--buildtype=release`)
|
||||||
3. Build with `cd build` and `ninja`
|
3. Build with `cd build` and `ninja`
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@ other data.
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
import re
|
import re
|
||||||
|
from enum import Enum
|
||||||
|
from tkinter.messagebox import askyesno
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from typing import Any, Dict, List, Tuple
|
from typing import Any, Dict, List, Tuple
|
||||||
|
|
||||||
|
@ -216,25 +218,36 @@ def save_keyframes(filename: str, keyframes: List[int]):
|
||||||
f.write("".join(f"{n}\n" for n in keyframes))
|
f.write("".join(f"{n}\n" for n in keyframes))
|
||||||
|
|
||||||
|
|
||||||
def try_get_keyframes(filename: str, default: str | List[int]) -> str | List[int]:
|
class GenKeyframesMode(Enum):
|
||||||
"""
|
NEVER = 0
|
||||||
Checks if a keyframes file for the given filename is present and, if so,
|
ALWAYS = 1
|
||||||
returns it. Otherwise, returns the given list of keyframes.
|
ASK = 2
|
||||||
"""
|
|
||||||
kffilename = make_keyframes_filename(filename)
|
|
||||||
|
|
||||||
return kffilename if os.path.exists(kffilename) else default
|
|
||||||
|
|
||||||
|
|
||||||
def get_keyframes(filename: str, clip: vs.VideoNode, **kwargs: Any) -> str:
|
def get_keyframes(filename: str, clip: vs.VideoNode, fallback: str | List[int],
|
||||||
|
generate: GenKeyframesMode = GenKeyframesMode.ASK, **kwargs: Any) -> str | List[int]:
|
||||||
"""
|
"""
|
||||||
When not already present, creates a keyframe file for the given clip next
|
Looks for a keyframes file for the given filename.
|
||||||
|
If no file was found, this function can generate a keyframe file for the given clip next
|
||||||
to the given filename using WWXD or Scxvid (see the make_keyframes docstring).
|
to the given filename using WWXD or Scxvid (see the make_keyframes docstring).
|
||||||
|
Whether or not keyframes are generated depends on the `generate` argument.
|
||||||
|
Depending on the `generate` argument, the function will
|
||||||
|
- always generate keyframes when no file was found
|
||||||
|
- never generate keyframes when no file was found
|
||||||
|
(and return the fallback keyframes instead)
|
||||||
|
- show a dialog to ask the user whether keyframes should be
|
||||||
|
generated or not
|
||||||
Additional keyword arguments are passed on to make_keyframes.
|
Additional keyword arguments are passed on to make_keyframes.
|
||||||
"""
|
"""
|
||||||
kffilename = make_keyframes_filename(filename)
|
kffilename = make_keyframes_filename(filename)
|
||||||
|
|
||||||
if not os.path.exists(kffilename):
|
if not os.path.exists(kffilename):
|
||||||
|
if generate == GenKeyframesMode.NEVER:
|
||||||
|
return fallback
|
||||||
|
if generate == GenKeyframesMode.ASK and not askyesno("Generate Keyframes", \
|
||||||
|
"No keyframes file was found for this video file.\nShould Aegisub detect keyframes from the video?\nThis will take a while.", default="no"):
|
||||||
|
return fallback
|
||||||
|
|
||||||
vs.core.log_message(vs.MESSAGE_TYPE_INFORMATION, "No keyframes file found, detecting keyframes...\n")
|
vs.core.log_message(vs.MESSAGE_TYPE_INFORMATION, "No keyframes file found, detecting keyframes...\n")
|
||||||
keyframes = make_keyframes(clip, **kwargs)
|
keyframes = make_keyframes(clip, **kwargs)
|
||||||
save_keyframes(kffilename, keyframes)
|
save_keyframes(kffilename, keyframes)
|
||||||
|
|
|
@ -84,6 +84,12 @@ public:
|
||||||
Color const& GetColor() const;
|
Color const& GetColor() const;
|
||||||
bool const& GetBool() const;
|
bool const& GetBool() const;
|
||||||
|
|
||||||
|
std::string const& GetDefaultString() const;
|
||||||
|
int64_t const& GetDefaultInt() const;
|
||||||
|
double const& GetDefaultDouble() const;
|
||||||
|
Color const& GetDefaultColor() const;
|
||||||
|
bool const& GetDefaultBool() const;
|
||||||
|
|
||||||
void SetString(const std::string);
|
void SetString(const std::string);
|
||||||
void SetInt(const int64_t);
|
void SetInt(const int64_t);
|
||||||
void SetDouble(const double);
|
void SetDouble(const double);
|
||||||
|
@ -96,6 +102,12 @@ public:
|
||||||
std::vector<Color> const& GetListColor() const;
|
std::vector<Color> const& GetListColor() const;
|
||||||
std::vector<bool> const& GetListBool() const;
|
std::vector<bool> const& GetListBool() const;
|
||||||
|
|
||||||
|
std::vector<std::string> const& GetDefaultListString() const;
|
||||||
|
std::vector<int64_t> const& GetDefaultListInt() const;
|
||||||
|
std::vector<double> const& GetDefaultListDouble() const;
|
||||||
|
std::vector<Color> const& GetDefaultListColor() const;
|
||||||
|
std::vector<bool> const& GetDefaultListBool() const;
|
||||||
|
|
||||||
void SetListString(std::vector<std::string>);
|
void SetListString(std::vector<std::string>);
|
||||||
void SetListInt(std::vector<int64_t>);
|
void SetListInt(std::vector<int64_t>);
|
||||||
void SetListDouble(std::vector<double>);
|
void SetListDouble(std::vector<double>);
|
||||||
|
@ -117,6 +129,7 @@ public:
|
||||||
: OptionValue(std::move(member_name)) \
|
: OptionValue(std::move(member_name)) \
|
||||||
, value(member_value), value_default(member_value) { } \
|
, value(member_value), value_default(member_value) { } \
|
||||||
type const& GetValue() const { return value; } \
|
type const& GetValue() const { return value; } \
|
||||||
|
type const& GetDefault() const { return value_default; } \
|
||||||
void SetValue(type new_val) { value = std::move(new_val); NotifyChanged(); } \
|
void SetValue(type new_val) { value = std::move(new_val); NotifyChanged(); } \
|
||||||
OptionType GetType() const { return OptionType::type_name; } \
|
OptionType GetType() const { return OptionType::type_name; } \
|
||||||
void Reset() { value = value_default; NotifyChanged(); } \
|
void Reset() { value = value_default; NotifyChanged(); } \
|
||||||
|
@ -141,6 +154,7 @@ CONFIG_OPTIONVALUE(Bool, bool)
|
||||||
: OptionValue(std::move(name)) \
|
: OptionValue(std::move(name)) \
|
||||||
, array(value), array_default(value) { } \
|
, array(value), array_default(value) { } \
|
||||||
std::vector<type> const& GetValue() const { return array; } \
|
std::vector<type> const& GetValue() const { return array; } \
|
||||||
|
std::vector<type> const& GetDefault() const { return array_default; } \
|
||||||
void SetValue(std::vector<type> val) { array = std::move(val); NotifyChanged(); } \
|
void SetValue(std::vector<type> val) { array = std::move(val); NotifyChanged(); } \
|
||||||
OptionType GetType() const { return OptionType::List##type_name; } \
|
OptionType GetType() const { return OptionType::List##type_name; } \
|
||||||
void Reset() { array = array_default; NotifyChanged(); } \
|
void Reset() { array = array_default; NotifyChanged(); } \
|
||||||
|
@ -156,6 +170,7 @@ CONFIG_OPTIONVALUE_LIST(Bool, bool)
|
||||||
|
|
||||||
#define CONFIG_OPTIONVALUE_ACCESSORS(ReturnType, Type) \
|
#define CONFIG_OPTIONVALUE_ACCESSORS(ReturnType, Type) \
|
||||||
inline ReturnType const& OptionValue::Get##Type() const { return As<OptionValue##Type>(OptionType::Type)->GetValue(); } \
|
inline ReturnType const& OptionValue::Get##Type() const { return As<OptionValue##Type>(OptionType::Type)->GetValue(); } \
|
||||||
|
inline ReturnType const& OptionValue::GetDefault##Type() const { return As<OptionValue##Type>(OptionType::Type)->GetDefault(); } \
|
||||||
inline void OptionValue::Set##Type(ReturnType v) { As<OptionValue##Type>(OptionType::Type)->SetValue(std::move(v)); }
|
inline void OptionValue::Set##Type(ReturnType v) { As<OptionValue##Type>(OptionType::Type)->SetValue(std::move(v)); }
|
||||||
|
|
||||||
CONFIG_OPTIONVALUE_ACCESSORS(std::string, String)
|
CONFIG_OPTIONVALUE_ACCESSORS(std::string, String)
|
||||||
|
|
|
@ -392,7 +392,7 @@
|
||||||
},
|
},
|
||||||
"VapourSynth" : {
|
"VapourSynth" : {
|
||||||
"Log Level": "Information",
|
"Log Level": "Information",
|
||||||
"Default Script" : "# This default script will load a video file using LWLibavSource.\n# It requires the `lsmas` plugin.\n# See ?data/automation/vapoursynth/aegisub_vs.py for more information.\n\nimport vapoursynth as vs\nimport time\nimport aegisub_vs as a\na.set_paths(locals())\n\nclip, videoinfo = a.wrap_lwlibavsource(filename)\nclip.set_output()\n__aegi_timecodes = videoinfo[\"timecodes\"]\n__aegi_keyframes = videoinfo[\"keyframes\"]\n\n# Uncomment the first following line to read keyframes from a file when present.\n#__aegi_keyframes = a.try_get_keyframes(filename, __aegi_keyframes)\n\n# Uncomment the following line to automatically generate keyframes at scene changes. This will take some time when first loading a video.\n#__aegi_keyframes = a.get_keyframes(filename, clip)\n\n# Check if the file has an audio track. This requires the `bas` plugin.\n__aegi_hasaudio = 1 if a.check_audio(filename) else 0"
|
"Default Script" : "# This default script will load a video file using LWLibavSource.\n# It requires the `lsmas` plugin.\n# See ?data/automation/vapoursynth/aegisub_vs.py for more information.\n\nimport vapoursynth as vs\nimport time\nimport aegisub_vs as a\na.set_paths(locals())\n\nclip, videoinfo = a.wrap_lwlibavsource(filename)\nclip.set_output()\n__aegi_timecodes = videoinfo[\"timecodes\"]\n__aegi_keyframes = videoinfo[\"keyframes\"]\n\n# Uncomment this line to make Aegisub look for a keyframes file for the video, or ask to detect keyframes on scene changes if no file was found.\n# You can also change the GenKeyframesMode. Valid values are NEVER, ALWAYS, and ASK.\n#__aegi_keyframes = a.get_keyframes(filename, clip, __aegi_keyframes, generate=a.GenKeyframesMode.ASK)\n\n# Check if the file has an audio track. This requires the `bas` plugin.\n__aegi_hasaudio = 1 if a.check_audio(filename) else 0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -392,7 +392,7 @@
|
||||||
},
|
},
|
||||||
"VapourSynth" : {
|
"VapourSynth" : {
|
||||||
"Log Level": "Information",
|
"Log Level": "Information",
|
||||||
"Default Script" : "# This default script will load a video file using LWLibavSource.\n# It requires the `lsmas` plugin.\n# See ?data/automation/vapoursynth/aegisub_vs.py for more information.\n\nimport vapoursynth as vs\nimport time\nimport aegisub_vs as a\na.set_paths(locals())\n\nclip, videoinfo = a.wrap_lwlibavsource(filename)\nclip.set_output()\n__aegi_timecodes = videoinfo[\"timecodes\"]\n__aegi_keyframes = videoinfo[\"keyframes\"]\n\n# Uncomment the first following line to read keyframes from a file when present.\n#__aegi_keyframes = a.try_get_keyframes(filename, __aegi_keyframes)\n\n# Uncomment the following line to automatically generate keyframes at scene changes. This will take some time when first loading a video.\n#__aegi_keyframes = a.get_keyframes(filename, clip)\n\n# Check if the file has an audio track. This requires the `bas` plugin.\n__aegi_hasaudio = 1 if a.check_audio(filename) else 0"
|
"Default Script" : "# This default script will load a video file using LWLibavSource.\n# It requires the `lsmas` plugin.\n# See ?data/automation/vapoursynth/aegisub_vs.py for more information.\n\nimport vapoursynth as vs\nimport time\nimport aegisub_vs as a\na.set_paths(locals())\n\nclip, videoinfo = a.wrap_lwlibavsource(filename)\nclip.set_output()\n__aegi_timecodes = videoinfo[\"timecodes\"]\n__aegi_keyframes = videoinfo[\"keyframes\"]\n\n# Uncomment this line to make Aegisub look for a keyframes file for the video, or ask to detect keyframes on scene changes if no file was found.\n# You can also change the GenKeyframesMode. Valid values are NEVER, ALWAYS, and ASK.\n#__aegi_keyframes = a.get_keyframes(filename, clip, __aegi_keyframes, generate=a.GenKeyframesMode.ASK)\n\n# Check if the file has an audio track. This requires the `bas` plugin.\n__aegi_hasaudio = 1 if a.check_audio(filename) else 0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -501,22 +501,36 @@ void VapourSynth(wxTreebook *book, Preferences *parent) {
|
||||||
|
|
||||||
auto video = p->PageSizer(_("Default Video Script"));
|
auto video = p->PageSizer(_("Default Video Script"));
|
||||||
|
|
||||||
|
auto make_default_button = [=](std::string optname, wxTextCtrl *ctrl) {
|
||||||
|
auto showdefault = new wxButton(p, -1, _("Set to Default"));
|
||||||
|
showdefault->Bind(wxEVT_BUTTON, [=](auto e) {
|
||||||
|
ctrl->SetValue(OPT_GET(optname)->GetDefaultString());
|
||||||
|
});
|
||||||
|
return showdefault;
|
||||||
|
};
|
||||||
|
|
||||||
auto vhint = new wxStaticText(p, wxID_ANY, _("This script will be executed to load video files that aren't\nVapourSynth scripts (i.e. end in .py or .vpy).\nThe filename variable stores the path to the file."));
|
auto vhint = new wxStaticText(p, wxID_ANY, _("This script will be executed to load video files that aren't\nVapourSynth scripts (i.e. end in .py or .vpy).\nThe filename variable stores the path to the file."));
|
||||||
p->sizer->Fit(p);
|
p->sizer->Fit(p);
|
||||||
vhint->Wrap(400);
|
vhint->Wrap(400);
|
||||||
video->Add(vhint, 0, wxALL, 5);
|
video->Add(vhint, 0, wxALL, 5);
|
||||||
video->AddSpacer(16);
|
p->CellSkip(video);
|
||||||
|
|
||||||
p->OptionAddMultiline(video, "Provider/Video/VapourSynth/Default Script");
|
auto vdef = p->OptionAddMultiline(video, "Provider/Video/VapourSynth/Default Script");
|
||||||
|
p->CellSkip(video);
|
||||||
|
|
||||||
|
video->Add(make_default_button("Provider/Video/VapourSynth/Default Script", vdef), wxSizerFlags().Right());
|
||||||
|
|
||||||
auto audio = p->PageSizer(_("Default Audio Script"));
|
auto audio = p->PageSizer(_("Default Audio Script"));
|
||||||
auto ahint = new wxStaticText(p, wxID_ANY, _("This script will be executed to load audio files that aren't\nVapourSynth scripts (i.e. end in .py or .vpy).\nThe filename variable stores the path to the file."));
|
auto ahint = new wxStaticText(p, wxID_ANY, _("This script will be executed to load audio files that aren't\nVapourSynth scripts (i.e. end in .py or .vpy).\nThe filename variable stores the path to the file."));
|
||||||
p->sizer->Fit(p);
|
p->sizer->Fit(p);
|
||||||
ahint->Wrap(400);
|
ahint->Wrap(400);
|
||||||
audio->Add(ahint, 0, wxALL, 5);
|
audio->Add(ahint, 0, wxALL, 5);
|
||||||
audio->AddSpacer(16);
|
p->CellSkip(audio);
|
||||||
|
|
||||||
p->OptionAddMultiline(audio, "Provider/Audio/VapourSynth/Default Script");
|
auto adef = p->OptionAddMultiline(audio, "Provider/Audio/VapourSynth/Default Script");
|
||||||
|
p->CellSkip(audio);
|
||||||
|
|
||||||
|
audio->Add(make_default_button("Provider/Audio/VapourSynth/Default Script", adef), wxSizerFlags().Right());
|
||||||
|
|
||||||
p->SetSizerAndFit(p->sizer);
|
p->SetSizerAndFit(p->sizer);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -156,7 +156,7 @@ wxControl *OptionPage::OptionAdd(wxFlexGridSizer *flex, const wxString &name, co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wxControl *OptionPage::OptionAddMultiline(wxSizer *sizer, const char *opt_name) {
|
wxTextCtrl *OptionPage::OptionAddMultiline(wxSizer *sizer, const char *opt_name) {
|
||||||
parent->AddChangeableOption(opt_name);
|
parent->AddChangeableOption(opt_name);
|
||||||
const auto opt = OPT_GET(opt_name);
|
const auto opt = OPT_GET(opt_name);
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ public:
|
||||||
|
|
||||||
void CellSkip(wxFlexGridSizer *flex);
|
void CellSkip(wxFlexGridSizer *flex);
|
||||||
wxControl *OptionAdd(wxFlexGridSizer *flex, const wxString &name, const char *opt_name, double min=0, double max=INT_MAX, double inc=1);
|
wxControl *OptionAdd(wxFlexGridSizer *flex, const wxString &name, const char *opt_name, double min=0, double max=INT_MAX, double inc=1);
|
||||||
wxControl *OptionAddMultiline(wxSizer *flex, const char *opt_name);
|
wxTextCtrl *OptionAddMultiline(wxSizer *flex, const char *opt_name);
|
||||||
void OptionChoice(wxFlexGridSizer *flex, const wxString &name, const wxArrayString &choices, const char *opt_name);
|
void OptionChoice(wxFlexGridSizer *flex, const wxString &name, const wxArrayString &choices, const char *opt_name);
|
||||||
void OptionBrowse(wxFlexGridSizer *flex, const wxString &name, const char *opt_name, wxControl *enabler = nullptr, bool do_enable = false);
|
void OptionBrowse(wxFlexGridSizer *flex, const wxString &name, const char *opt_name, wxControl *enabler = nullptr, bool do_enable = false);
|
||||||
void OptionFont(wxSizer *sizer, std::string opt_prefix);
|
void OptionFont(wxSizer *sizer, std::string opt_prefix);
|
||||||
|
|
Loading…
Reference in a new issue