API — include/icore
The public contract of 3 header(s) under include/icore — 4 class/struct definition(s), 38 declaration(s). Each section shows the header's banner and its public (and protected-virtual) surface exactly as the file writes it.
| Header | Defines | Declarations | Bases |
|---|---|---|---|
Application.h | StartupProgress, Application | 14 | — |
EditorWindow.h | EditorWindow | 10 | — |
License.h | License | 14 | — |
Application.h#
include/icore/Application.h
Application.h -- the ICoreBlocks SDK entry point.
icore::Applicationis a LIFETIME SCOPE, not a GUI-toolkit application object. It owns the event loop and it is the only factory for editor windows. Nothing else in the public surface can bring an editor window into being.This header is part of include/icore/, which is toolkit-free by contract: the widget toolkit currently backing the implementation is scheduled for removal, so no type belonging to it may appear here -- not in a signature, not in a base class, not behind a "native handle" accessor. Everything the implementation needs lives behind Impl, which is declared here and defined only in the corresponding translation unit under src/.
StartupProgress#
Application.h:30 · struct · 0 declaration(s)
/ One step of a session coming up, for a caller that shows its own startup / screen while it does.
struct StartupProgress {
public:
/// What startup is doing now, as a short sentence ("Opening Motor Rig").
/// Valid only for the duration of the call. Empty when `ready` is true.
std::string_view status;
/// How far through startup this step begins, from 0 to 1. Steps arrive in
/// increasing order, but a step's size is not an estimate of its time.
double fraction = 0.0;
/// True exactly once, on the last report: startup has finished and the
/// first window should be shown now.
bool ready = false;
};
};
Application#
Application.h:69 · class · pImpl · 14 declaration(s)
/ The process-wide lifetime scope for an ICoreBlocks session.
class Application {
public:
/// Opens the session.
///
/// @param argc argument count, exactly as received by `main`.
/// @param argv argument vector, exactly as received by `main`.
///
/// Both are consumed by reference internally and may be inspected or
/// rewritten (recognised platform switches are removed). The storage
/// they point at must outlive the Application -- passing `main`'s own
/// argc/argv satisfies this; passing a local copy that goes out of
/// scope does not.
Application(int argc, char** argv);
/// Opens the session, as above, and reports its progress to
/// `onStartupProgress` so the caller can show a startup screen.
///
/// The reports, in order:
///
/// * **Steps**, from inside this constructor, from inside the first
/// createEditorWindow() (building the window is most of startup's cost)
/// and from the first tick(). The first arrives once the toolkit and the
/// user's theme are up, which is the earliest moment a window can be
/// made and drawn in the right colours. Each is followed by a short pump
/// of the toolkit, run until it goes quiet (at most 0.8 s after the
/// first report and 150 ms after each later one), so a window the
/// callback shows or changes is on screen before startup continues --
/// a new window can take several rounds of messages to draw its first
/// frame.
/// * **Ready**, exactly once, from the first tick(): the last project has
/// been reopened, and nothing else has been raised yet. Show the first
/// EditorWindow here. The project launcher and the sign-in prompt, when
/// either is due, come straight after this call and land in front of it.
///
/// A headless run (`--console`, `--check-updates`) makes no step reports,
/// only the ready one. Nobody would see a startup screen there.
///
/// Create the first EditorWindow before calling run() and do not show it
/// yet; show it from the ready report. The callback is released after that
/// report, together with anything it holds.
///
/// @param onStartupProgress the caller's startup screen. Called on the
/// thread that constructs the Application. It must not call
/// createEditorWindow(), run(), or tick().
Application(int argc, char** argv,
std::function<void(const StartupProgress&)> onStartupProgress);
/// Closes the session and tears down the event loop.
///
/// @pre Every EditorWindow created by this Application has already been
/// destroyed.
~Application();
Application(const Application&) = delete;
Application& operator=(const Application&) = delete;
Application(Application&&) = delete;
Application& operator=(Application&&) = delete;
/// Creates a new editor window, owned by the caller.
///
/// The window is created hidden; call EditorWindow::show() to put it on
/// screen. This is the only way to obtain an EditorWindow -- its
/// constructor is private and this class is its only friend.
///
/// @return A handle to the new window. Never null; failure to create
/// the window throws rather than returning a null handle.
/// @note The returned window must not outlive this Application.
[[nodiscard]] std::unique_ptr<EditorWindow> createEditorWindow();
/// Runs the session to completion and returns the exit code `main` should
/// return.
///
/// A convenience only, and deliberately a thin one -- it is exactly
/// `while (isRunning()) tick(); return exitCode();`. The loop below is the
/// primitive; this is the two-line spelling of it for callers that have no
/// work of their own to interleave.
///
/// Blocks. Call once, from the thread that constructed the Application.
///
/// @note In a browser build there is no `while` to block in: the page's
/// thread belongs to the browser, so run() hands each tick() to the
/// browser's frame loop and never returns -- the page ends with
/// exitCode() when the session does. Code after run() is not reached
/// there; teardown belongs in the last tick(), which is where the
/// Application already does its own.
/// @warning In a browser build the Application, and anything the session
/// uses, must have static or heap storage -- not main()'s stack. The
/// hand-over to the browser unwinds main() and runs the destructors
/// of everything in its frame; a build that breaks this aborts with
/// a message saying so.
int run();
// --- the loop -----------------------------------------------------------
//
// THE CALLER OWNS THE LOOP. These three are what make that true: the
// Application never blocks in a toolkit loop it controls, it advances one
// slice at a time when asked, and it reports whether it should be asked
// again. That is the whole inversion -- the UI toolkit is something this
// process drives, not something this process is handed to.
//
// It is also what lets a host with its own clock -- a simulation stepper, a
// frame loop, an embedder that already owns main() -- run an ICoreBlocks
// editor without surrendering control of the process to it:
//
// @code
// while (app.isRunning()) {
// app.tick(); // the UI gets a slice
// myScheduler.step(); // ...and so does everything else
// }
// return app.exitCode();
// @endcode
/// Advances the session by one slice: dispatches whatever UI and platform
/// events are pending, then returns.
///
/// @param waitMs How long to wait for an event when none is pending, in
/// milliseconds. The default idles rather than spins, which
/// is what an editor wants -- an idle window costs no CPU.
/// Pass 0 for a non-blocking poll, which is what a caller
/// with its own frame budget wants instead.
///
/// Call from the thread that constructed the Application.
void tick(int waitMs = 16);
/// Whether the session is still live -- i.e. whether to call tick() again.
///
/// Becomes false once requestQuit() has been called, or once the last
/// window has closed.
[[nodiscard]] bool isRunning() const noexcept;
/// Ends the session: isRunning() becomes false and the next tick() is the
/// caller's last.
///
/// Does not tear anything down by itself and does not return through the
/// caller's loop early -- it sets the intent, and the loop the caller wrote
/// is what observes it. Safe to call more than once; the first code wins,
/// so a shutdown already under way is not relabelled by a later caller.
void requestQuit(int exitCode = 0);
/// The code passed to requestQuit(), or 0 if the session ended on its own.
///
/// Meaningful once isRunning() is false.
[[nodiscard]] int exitCode() const noexcept;
/// The live Application, or null when this process has none.
///
/// For code deep inside the implementation that has to end the session and
/// has no path back to the object -- see ICoreStudioRegistry. Nullable on
/// purpose: an embedder, or a test binary, may run parts of this codebase
/// with no Application in scope at all, and such a caller needs to be able
/// to ask without asserting.
[[nodiscard]] static Application* liveInstance() noexcept;
private:
class Impl; // the two-line residue; state lives here
std::unique_ptr<Impl> impl;
};
EditorWindow.h#
include/icore/EditorWindow.h
EditorWindow.h -- a top-level ICoreBlocks editor window.
An EditorWindow is created only by icore::Application::createEditorWindow(). The constructor is private and Application is the sole friend, so a window cannot exist outside the lifetime scope that owns the event loop it needs.
This header is part of include/icore/, which is toolkit-free by contract. The widget toolkit currently backing the implementation is scheduled for removal, so none of its types appear here -- text crossing this boundary is UTF-8 std::string / std::string_view, and the widget itself stays behind Impl, declared here and defined only under src/.
See DEVELOPER_GUIDELINES.md.
EditorWindow#
EditorWindow.h:39 · class · pImpl · 10 declaration(s)
/ A top-level editor window.
class EditorWindow {
public:
/// Closes the window and destroys it.
~EditorWindow();
EditorWindow(const EditorWindow&) = delete;
EditorWindow& operator=(const EditorWindow&) = delete;
EditorWindow(EditorWindow&&) = delete;
EditorWindow& operator=(EditorWindow&&) = delete;
/// Sets the window title.
///
/// @param title UTF-8 text. Copied; the view need not outlive the call
/// and need not be null-terminated.
void setTitle(std::string_view title);
/// Puts the window on screen.
///
/// Windows are created hidden, so this is what makes a newly created
/// window visible. Calling it on an already-visible window raises and
/// focuses it instead.
void show();
/// Closes the window, exactly as closing it from the window system does.
///
/// Does NOT destroy it -- the handle still owns the window and show() puts
/// it back on screen. Destroying the handle is what destroys the window.
///
/// @note Closing the last open window of an Application ends that
/// Application's session, so run() returns after this.
void close();
/// Opens a project folder in this window.
///
/// Takes the same path every other project switch in the editor takes, so
/// it may raise the prompts that path raises -- save-before-switch, or
/// "not a project folder" -- parented on this window.
///
/// @param utf8Path Absolute path to the project folder, UTF-8. Copied.
/// @return true when this window is showing that project afterwards --
/// including when it already was. false when the switch was
/// cancelled or failed.
[[nodiscard]] bool openProject(std::string_view utf8Path);
/// Installs a handler to run when this window is closing.
///
/// Fires for a close from the window system and for close() alike, while
/// the window is still up. std::function rather than any toolkit's signal,
/// which is what lets this cross the SDK boundary at all.
///
/// One handler, replaced rather than accumulated -- pass an empty
/// std::function to clear it. Installing one does not disturb the
/// Application's own end-of-session bookkeeping; that is tracked
/// separately and cannot be displaced from here.
void onCloseRequested(std::function<void()> fn);
private:
class Impl; // the two-line residue; state lives here
std::unique_ptr<Impl> impl;
};
License.h#
include/icore/License.h
License.h -- what this copy of ICoreBlocks is allowed to do.
icore::Licenseis a read-only view of the licence the running process resolved at start-up. It answers questions; it never acquires, refreshes or stores anything, and there is no way from this header to change what it says.This header is part of include/icore/, which is toolkit-free by contract: no type belonging to the widget toolkit may appear here, and no header from inside the implementation tree either. Notifications are
std::function, exactly as EditorWindow::onCloseRequested is, rather than any signal type.See DEVELOPER_GUIDELINES.md.
License#
License.h:73 · class · pImpl · 14 declaration(s)
/ The process-wide licence view.
class License {
public:
/// The one instance. Never null, never replaced.
static License& instance();
LicenseState state() const;
/// Whether one capability is available. This is the gate.
bool has(Feature feature) const;
/// Whether one code-generation target may be produced -- "rust", "vhdl",
/// "st", and the rest. Case-insensitive.
///
/// @note Code generation is not a paid capability: every standard tier
/// carries every generator, so this normally returns true. It exists
/// because a specific deployment can be restricted, and because a
/// headless run bypasses the user interface entirely. Do not build
/// UI that hides generators by tier.
bool allowsCodegenTarget(std::string_view target) const;
/// Whether the work produced may be used commercially.
///
/// @note Read, never inferred. This rests on the attestation signed at
/// purchase and held by the licensing service; it is not derivable
/// from the tier name and must never be guessed at from usage.
bool allowsCommercialUse() const;
/// The tier identifier -- FOR DISPLAY AND DIAGNOSTICS ONLY. See Feature.
std::string tierId() const;
/// The tier's human-readable name, e.g. "Commercial — Individual".
std::string tierDisplayName() const;
/// The plan and billing period, e.g. "individual-yearly" and "yearly".
///
/// @warning Billing facts, not capabilities. A monthly and a yearly licence
/// of the same tier unlock byte-identical software. Show them;
/// drive a renewal prompt from them; branch on neither.
std::string plan() const;
std::string billingPeriod() const;
/// When the current licence token stops being valid, or nothing when there
/// is no licence.
std::optional<std::chrono::system_clock::time_point> expiresAt() const;
/// Whole days left before `Grace` becomes `Expired`; 0 in every other
/// state. Rounded up, so the last partial day still reads as 1.
int graceDaysRemaining() const;
/// Installs a callback for state changes. `std::function` rather than any
/// toolkit's signal, for the same reason EditorWindow::onCloseRequested is.
///
/// Installing one replaces the previous; pass an empty std::function to
/// clear it. The callback runs on the thread the change happened on.
void onStateChanged(std::function<void(LicenseState)> fn);
License(const License&) = delete;
License& operator=(const License&) = delete;
private:
class Impl; // the two-line residue; state lives here
std::unique_ptr<Impl> impl;
};
File-scope declarations#
// / A gated capability.
// /
// / @warning Gate on one of these, NEVER on tierId(). A licence carries
// / per-licence overrides, so the tier name does not determine the
// / feature set: `license.tierId() == "commercial_team_sdk"` compiles
// / perfectly, passes the test its author wrote (their licence is the
enum class Feature {
HdlExport,
SdkAccess,
SdkDownload,
CustomBlockAuthoring,
PluginLoad,
HeadlessCli,
Redistribution,
WatermarkGeneratedCode
};
// / Where the licence stands right now.
// /
// / `Grace` is not an error state: it means the token's validity lapsed while
// / the licensing service could not be reached, and the application is FULLY
// / FUNCTIONAL for thirty days. `Expired` means that window is used up (or the
// / licence was revoked): models still open and still save, while simulation
enum class LicenseState {
Unlicensed,
Trial,
Licensed,
Grace,
Expired
};