Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions qt-cli/src/newitem/handler_item.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ type NewItemResponse struct {
DryRun bool `json:"dryRun" binding:"required"`
}

type ValidateRequest struct {
Type string `json:"type"`
Name string `json:"name"`
WorkingDir string `json:"workingDir"`
}

type PostNewItemContext struct {
name string
workingDir string
Expand Down Expand Up @@ -89,15 +95,16 @@ func PostItems(c *gin.Context) {
}

func PostItemsValidate(c *gin.Context) {
context := PreparePostItemsContext(c)
if context == nil {
var req ValidateRequest
if err := c.ShouldBindJSON(&req); err != nil {
rest.ReplyErrorMsg(c, err.Error())
return
}

issues := generator.Validate(generator.ValidatorIn{
Name: context.name,
WorkingDir: context.workingDir,
TypeId: context.preset.GetTypeId(),
Name: req.Name,
WorkingDir: req.WorkingDir,
TypeId: preset.TargetTypeFromString(req.Type),
})

if len(issues) != 0 {
Expand Down
5 changes: 5 additions & 0 deletions qt-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@
"command": "qt-core.openInLinguist",
"title": "%qt-core.command.openInLinguist.title%",
"category": "Qt"
},
{
"command": "qt-core.openExamplesBrowser",
"title": "%qt-core.command.openExamplesBrowser.title%",
"category": "Qt"
}
],
"customEditors": [
Expand Down
3 changes: 2 additions & 1 deletion qt-core/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
"qt-core.command.registerQtByQtpaths.title": "Register Qt (by qtpaths or qmake)",
"qt-core.command.createNewItem.title": "Create a new project or file",
"qt-core.command.openInLinguist.title": "Open current file in Qt Linguist",
"qt-core.command.reportIssue.title": "Report an issue"
"qt-core.command.reportIssue.title": "Report an issue",
"qt-core.command.openExamplesBrowser.title": "Open Qt examples"
}
Binary file added qt-core/res/icons/qt-codesample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions qt-core/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
} from '@/small-commands';
import { checkQtpathsInEnvPath, registerQtByQtpaths } from '@/qtpaths';
import { checkVcpkg } from '@/vcpkg';
import { registerOpenExBrowserCommand } from '@/webview/ex-browser/controller';
import { registerCreateNewItemPanelCommand } from '@/webview/new-item/panel';
import { registerQrcEditorProvider } from '@/webview/qrc-editor/editor-provider';
import { registerQmlTraceProvider } from '@/webview/qml-trace/editor-provider';
Expand Down Expand Up @@ -64,6 +65,7 @@ export async function activate(context: vscode.ExtensionContext) {
registerRegisterQtCommand(),
registerRegisterQtByPathCommand(),
registerOpenInLinguistCommand(),
registerOpenExBrowserCommand(context),
registerCreateNewItemPanelCommand(context),
vscode.languages.registerColorProvider('qss', createColorProvider()),
reportIssueCommand()
Expand Down
199 changes: 199 additions & 0 deletions qt-core/src/fs-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// Copyright (C) 2026 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only

import * as fs from 'fs';
import * as path from 'path';
import * as vscode from 'vscode';

export function fsDir(first: string | vscode.Uri, ...rest: string[]) {
return new DirWrapper(resolvePath(first, ...rest));
}

export function fsFile(first: string | vscode.Uri, ...rest: string[]) {
return new FileWrapper(resolvePath(first, ...rest));
}

// internal classes
class DirWrapper {
constructor(private readonly _dirPath: string) {}

public toString() {
return this._dirPath;
}

public toUri() {
return vscode.Uri.file(this._dirPath);
}

public stat(): fs.Stats | undefined {
try {
return fs.statSync(this._dirPath);
} catch {
return undefined;
}
}

public exists(): boolean {
try {
const stat = fs.statSync(this._dirPath);
return stat.isDirectory();
} catch {
return false;
}
}

public subDirPaths(): string[] {
return fs
.readdirSync(this._dirPath, { withFileTypes: true })
.filter((e) => e.isDirectory())
.map((e) => path.join(e.parentPath, e.name));
}

public subDirNames(): string[] {
return fs
.readdirSync(this._dirPath, { withFileTypes: true })
.filter((e) => e.isDirectory())
.map((e) => e.name);
}

public allFilePaths(name: string): string[] {
const found: string[] = [];

this._walkAllDirs((fullPath: string, e: fs.Dirent) => {
if (e.isFile() && e.name === name) {
found.push(fullPath);
}
});

return found;
}

public copyAll(destDir: string) {
DirWrapper._copyAllDeep(this._dirPath, destDir);
}

// vscode commands
public openAsWorkspace(option: { newWindow?: boolean } = {}) {
if (option.newWindow) {
return vscode.commands.executeCommand(
'vscode.openFolder',
this.toUri(),
true
);
} else {
return vscode.workspace.updateWorkspaceFolders(
vscode.workspace.workspaceFolders?.length ?? 0,
null,
{ uri: this.toUri() }
);
}
}

public revealInFileManager() {
return vscode.env.openExternal(this.toUri());
}

// private methods
private _walkAllDirs(task: (fullPath: string, e: fs.Dirent) => void) {
function walk(dir: string) {
const entries = fs.readdirSync(dir, { withFileTypes: true });

for (const e of entries) {
const fullPath = path.join(dir, e.name);
if (e.isDirectory()) {
walk(fullPath);
continue;
}

task(fullPath, e);
}
}

walk(this._dirPath);
}

private static _copyAllDeep(srcDir: string, destDir: string) {
if (!fs.existsSync(destDir)) {
fs.mkdirSync(destDir, { recursive: true });
}

const entries = fs.readdirSync(srcDir, { withFileTypes: true });

for (const entry of entries) {
const srcPath = path.join(srcDir, entry.name);
const destPath = path.join(destDir, entry.name);

if (!entry.isDirectory()) {
fs.copyFileSync(srcPath, destPath);
continue;
}

DirWrapper._copyAllDeep(srcPath, destPath);
}
}
}

class FileWrapper {
constructor(private readonly _filePath: string) {}

public toString() {
return this._filePath;
}

public toUri() {
return vscode.Uri.file(this._filePath);
}

public stat(): fs.Stats | undefined {
try {
return fs.statSync(this._filePath);
} catch {
return undefined;
}
}

public exists(): boolean {
try {
const stat = fs.statSync(this._filePath);
return stat.isFile();
} catch {
return false;
}
}

public readAll() {
return fs.readFileSync(this._filePath);
}

// vscode commands
public openInEditor(options?: vscode.TextDocumentShowOptions) {
return vscode.window.showTextDocument(
this.toUri(),
options ?? {
viewColumn: vscode.ViewColumn.Beside,
preserveFocus: true,
preview: true
}
);
}

public openExternal() {
return vscode.env.openExternal(this.toUri());
}

public openInSimpleBrowser(viewColumn?: vscode.ViewColumn) {
return vscode.commands.executeCommand(
'simpleBrowser.api.open',
this.toUri(),
{
viewColumn: viewColumn ?? vscode.ViewColumn.Beside
}
);
}
}

// helper
function resolvePath(first: string | vscode.Uri, ...rest: string[]) {
const base = first instanceof vscode.Uri ? first.fsPath : first;
return path.join(base, ...rest);
}
9 changes: 9 additions & 0 deletions qt-core/src/texts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,12 @@ export const newItem = {
' and check the terminal for error messages',
workingDirDialogTitle: 'Select directory'
};

export const exBrowser = {
tabText: 'Qt examples',

specialCategory: {
all: 'All',
featured: 'Featured'
}
};
18 changes: 18 additions & 0 deletions qt-core/src/webview/ex-browser/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (C) 2026 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only

import * as vscode from 'vscode';

export const DOCS_DIR_NAME = 'Docs';

export const EX_DIR_NAME = 'Examples';
export const EX_MANIFEST_FILE_NAME = 'examples-manifest.xml';

export const DEMO_INJECTED_TAG_NAME = 'demo';
export const DEMO_MANIFEST_FILE_NAME = 'demos-manifest.xml';
export const DEMO_INJECTED_CATEGORY_NAME = 'Demo';

export const FALLBACK_IMAGE_FILE_IN_RES = 'qt-codesample.png';

export const WEBVIEW_PANEL_COLUMN = vscode.ViewColumn.One;
export const WEBVIEW_PANEL_VIEW_TYPE = 'ViewTypeExBrowser';
Loading
Loading