Skip to content

Commit e92a6a4

Browse files
refactor(webapp): refactor VNC encoding selection settings
* refactor the encoding selector to is able to select one encoding at a time (we have the same in RDM) * add `Default` encoding to the encoding selector list which defaults to sending all encodings to the server, and let it select one (we have the same in RDM) * added logic to hide the `pixel format` option if _Tight JPEG_ or _Tight PNG_ is selected. These doesn't work well together as Devolutions/IronVNC#924 bug happens. * removed _Tight PNG_ encoding from the encoding selector and added a checkbox with the same appearing only when `Tight` encoding is selected. _Tight PNG_ is more like an extension for _Tight_ (like `JPEG`), rather than a seperate thign. So, it makes sense to keep it as a setting for _Tight_ encoding. * added hiding `JPEG` option for other encodings than _Tight_, as it's only matters when _Tight_ is enabled.
1 parent 78ef149 commit e92a6a4

16 files changed

+308
-61
lines changed

webapp/src/client/app/modules/web-client/form/form-components/vnc/vnc-form.component.html

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,39 @@
4040
</div>
4141

4242
<div class="col-12 gateway-form-group">
43-
<web-client-enabled-encodings-control [parentForm]="form"
44-
[inputFormData]="inputFormData"></web-client-enabled-encodings-control>
43+
<web-client-enabled-encoding-control
44+
[parentForm]="form"
45+
[inputFormData]="inputFormData"
46+
(controlReady)="subscribeToSelectedEncodingChanges()"></web-client-enabled-encoding-control>
4547
</div>
4648

47-
<div class="col-12 gateway-form-group">
48-
<web-client-color-format-control [parentForm]="form"
49-
[inputFormData]="inputFormData"></web-client-color-format-control>
49+
@if (showTightOptions) {
50+
<div class="col-12 gateway-form-group">
51+
<web-client-tight-png-enabled-control [parentForm]="form"
52+
[inputFormData]="inputFormData"
53+
(controlReady)="subscribeToPngEnabledChanges()"></web-client-tight-png-enabled-control>
54+
</div>
55+
56+
<div class="col-12 gateway-form-group">
5057
<web-client-jpeg-quality-level-control [parentForm]="form"
51-
[inputFormData]="inputFormData"></web-client-jpeg-quality-level-control>
52-
</div>
58+
[inputFormData]="inputFormData"
59+
(controlReady)="subscribeToJpegEnabledChanges()"></web-client-jpeg-quality-level-control>
60+
</div>
61+
}
62+
63+
@if (showPixelFormatSelector) {
64+
<div class="col-12 gateway-form-group">
65+
<web-client-color-format-control [parentForm]="form"
66+
[inputFormData]="inputFormData"
67+
[disabled]="pixelFormatSelectorDisabled"
68+
disabledTooltip="Tight JPEG and Tight PNG do not support custom pixel formats."
69+
></web-client-color-format-control>
70+
</div>
71+
}
5372

5473
<div class="col-12 gateway-form-group">
5574
<web-client-enable-cursor-control [parentForm]="form"
56-
[inputFormData]="inputFormData"></web-client-enable-cursor-control>
75+
[inputFormData]="inputFormData"></web-client-enable-cursor-control>
5776
</div>
5877

5978
<div *ngIf="showExtendedClipboardCheckbox" class="col-12 gateway-form-group">

webapp/src/client/app/modules/web-client/form/form-components/vnc/vnc-form.component.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
22
import { FormGroup } from '@angular/forms';
33

44
import { BaseComponent } from '@shared/bases/base.component';
5+
import { Encoding } from '@shared/enums/encoding.enum';
56
import { VncAuthMode } from '@shared/enums/web-client-auth-mode.enum';
67
import { WebFormService } from '@shared/services/web-form.service';
78
import { SelectItem } from 'primeng/api';
@@ -14,6 +15,11 @@ interface FormInputVisibility {
1415
showPasswordInput?: boolean;
1516
}
1617

18+
interface TightOptions {
19+
jpeg: boolean;
20+
png: boolean;
21+
}
22+
1723
@Component({
1824
selector: 'vnc-form',
1925
templateUrl: 'vnc-form.component.html',
@@ -31,9 +37,16 @@ export class VncFormComponent extends BaseComponent implements OnInit {
3137
};
3238

3339
showMoreSettings = false;
40+
showPixelFormatSelector = false;
41+
showTightOptions = false;
3442
showExtendedClipboardCheckbox = false;
3543
showAutoClipboardCheckbox = false;
3644

45+
pixelFormatSelectorDisabled = false;
46+
47+
selectedEncoding = Encoding.Default;
48+
tightOptions: TightOptions = { jpeg: true, png: true };
49+
3750
constructor(
3851
private formService: WebFormService,
3952
private cdr: ChangeDetectorRef,
@@ -97,6 +110,75 @@ export class VncFormComponent extends BaseComponent implements OnInit {
97110
this.showAutoClipboardCheckbox = new UAParser().getEngine().name === 'Blink' && window.isSecureContext;
98111
}
99112

113+
subscribeToSelectedEncodingChanges(): void {
114+
this.form
115+
.get('enabledEncoding')
116+
.valueChanges.pipe(
117+
takeUntil(this.destroyed$),
118+
startWith(this.form.get('enabledEncoding').value as Encoding),
119+
switchMap((encoding: Encoding) => {
120+
this.showPixelFormatSelector = encoding !== Encoding.Default;
121+
this.showTightOptions = encoding === Encoding.Tight;
122+
this.selectedEncoding = encoding;
123+
124+
return of(undefined);
125+
}),
126+
)
127+
.subscribe({
128+
error: (error) => console.error('Failed to subscribe to selected encoding changes', error),
129+
});
130+
}
131+
132+
subscribeToJpegEnabledChanges(): void {
133+
this.form
134+
.get('jpegEnabled')
135+
.valueChanges.pipe(
136+
takeUntil(this.destroyed$),
137+
startWith(this.form.get('jpegEnabled').value as boolean),
138+
switchMap((jpegEnabled: boolean) => {
139+
this.tightOptions.jpeg = jpegEnabled;
140+
this.updatePixelFormatOptionState();
141+
this.cdr.detectChanges();
142+
143+
return of(undefined);
144+
}),
145+
)
146+
.subscribe({
147+
error: (error) => console.error('Failed to subscribe to jpeg enabled changes', error),
148+
});
149+
}
150+
151+
subscribeToPngEnabledChanges(): void {
152+
this.form
153+
.get('pngEnabled')
154+
.valueChanges.pipe(
155+
takeUntil(this.destroyed$),
156+
startWith(this.form.get('pngEnabled').value as boolean),
157+
switchMap((pngEnabled: boolean) => {
158+
this.tightOptions.png = pngEnabled;
159+
this.updatePixelFormatOptionState();
160+
this.cdr.detectChanges();
161+
162+
return of(undefined);
163+
}),
164+
)
165+
.subscribe({
166+
error: (error) => console.error('Failed to subscribe to jpeg enabled changes', error),
167+
});
168+
}
169+
170+
private updatePixelFormatOptionState(): void {
171+
const { jpeg, png } = this.tightOptions;
172+
173+
// Disable PixelFormat option for Tight JPEG and Tight PNG.
174+
if (this.selectedEncoding === Encoding.Tight && (jpeg || png)) {
175+
this.pixelFormatSelectorDisabled = true;
176+
return;
177+
}
178+
179+
this.pixelFormatSelectorDisabled = false;
180+
}
181+
100182
private subscribeToAuthModeChanges(): void {
101183
this.form
102184
.get('authMode')

webapp/src/client/app/modules/web-client/form/form-controls/color-format-control/color-format-control.component.ts

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, Input, OnInit } from '@angular/core';
1+
import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
22
import { FormGroup } from '@angular/forms';
33
import { BaseComponent } from '@shared/bases/base.component';
44
import { ColorFormat } from '@shared/enums/color-format.enum';
@@ -10,9 +10,11 @@ import { WebFormService } from '@shared/services/web-form.service';
1010
templateUrl: 'color-format-control.component.html',
1111
styleUrls: ['color-format-control.component.scss'],
1212
})
13-
export class ColorFormatControlComponent extends BaseComponent implements OnInit {
13+
export class ColorFormatControlComponent extends BaseComponent implements OnInit, OnDestroy, OnChanges {
1414
@Input() parentForm: FormGroup;
1515
@Input() inputFormData;
16+
@Input() disabled: boolean;
17+
@Input() disabledTooltip: string;
1618

1719
colorFormatOptions: SelectItemWithTooltip[];
1820

@@ -22,16 +24,51 @@ export class ColorFormatControlComponent extends BaseComponent implements OnInit
2224

2325
ngOnInit(): void {
2426
this.colorFormatOptions = this.formService.getColorFormatOptions();
25-
this.formService.addControlToForm({
26-
formGroup: this.parentForm,
27-
controlName: 'colorFormat',
28-
inputFormData: this.inputFormData,
29-
isRequired: false,
30-
defaultValue: ColorFormat.Default,
31-
});
27+
28+
if (!this.parentForm.contains('colorFormat')) {
29+
this.formService.addControlToForm({
30+
formGroup: this.parentForm,
31+
controlName: 'colorFormat',
32+
inputFormData: this.inputFormData,
33+
isRequired: false,
34+
defaultValue: ColorFormat.Default,
35+
});
36+
} else {
37+
this.parentForm.get('colorFormat').enable();
38+
}
39+
40+
if (this.disabled) {
41+
this.parentForm.get('colorFormat').disable();
42+
}
43+
}
44+
45+
ngOnChanges(changes: SimpleChanges): void {
46+
const disabled = changes.disabled;
47+
if (disabled) {
48+
// First `ngOnChanges` runs before `ngOnInit`.
49+
if (disabled.firstChange) {
50+
return;
51+
}
52+
53+
if (disabled.currentValue) {
54+
this.parentForm.get('colorFormat').disable();
55+
} else {
56+
this.parentForm.get('colorFormat').enable();
57+
}
58+
}
59+
}
60+
61+
ngOnDestroy() {
62+
super.ngOnDestroy();
63+
// Disable the control to ignore it when reading the form. At the same time, the value is preserved.
64+
this.parentForm.get('colorFormat').disable();
3265
}
3366

3467
getSelectedTooltip(): string {
68+
if (this.disabled) {
69+
return this.disabledTooltip;
70+
}
71+
3572
const selectedOptionValue = this.parentForm.get('colorFormat')?.value;
3673
return this.colorFormatOptions.find((item) => item.value === selectedOptionValue)?.tooltipText || '';
3774
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<div [formGroup]="parentForm">
2+
<label for="enabledEncoding">Enabled Encoding</label>
3+
<div class="gateway-form-input">
4+
<p-dropdown id="enabledEncoding"
5+
styleClass="full-with-dropdown"
6+
appendTo="body"
7+
formControlName="enabledEncoding"
8+
[options]="supportedEncodings"/>
9+
</div>
10+
</div>
File renamed without changes.
Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
import { Component, Input, OnInit } from '@angular/core';
1+
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
22
import { FormGroup } from '@angular/forms';
33
import { Encoding } from '@gateway/shared/enums/encoding.enum';
44
import { BaseComponent } from '@shared/bases/base.component';
55
import { WebFormService } from '@shared/services/web-form.service';
66
import { SelectItem } from 'primeng/api';
77

88
@Component({
9-
selector: 'web-client-enabled-encodings-control',
10-
templateUrl: 'enabled-encodings-control.component.html',
11-
styleUrls: ['enabled-encodings-control.component.scss'],
9+
selector: 'web-client-enabled-encoding-control',
10+
templateUrl: 'enabled-encoding-control.component.html',
11+
styleUrls: ['enabled-encoding-control.component.scss'],
1212
})
13-
export class EnabledEncodingsControlComponent extends BaseComponent implements OnInit {
13+
export class EnabledEncodingControlComponent extends BaseComponent implements OnInit {
1414
@Input() parentForm: FormGroup;
1515
@Input() inputFormData;
1616

17+
@Output() controlReady = new EventEmitter<void>();
18+
1719
supportedEncodings: SelectItem[];
1820

1921
constructor(private formService: WebFormService) {
@@ -24,10 +26,12 @@ export class EnabledEncodingsControlComponent extends BaseComponent implements O
2426
this.supportedEncodings = this.formService.getSupportedEncodings();
2527
this.formService.addControlToForm({
2628
formGroup: this.parentForm,
27-
controlName: 'enabledEncodings',
29+
controlName: 'enabledEncoding',
2830
inputFormData: this.inputFormData,
2931
isRequired: false,
30-
defaultValue: Encoding.getAllEncodings(),
32+
defaultValue: Encoding.Default,
3133
});
34+
35+
this.controlReady.emit();
3236
}
3337
}

webapp/src/client/app/modules/web-client/form/form-controls/enabled-encodings-control/enabled-encodings-control.component.html

Lines changed: 0 additions & 9 deletions
This file was deleted.
Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, Input, OnInit } from '@angular/core';
1+
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
22
import { FormGroup } from '@angular/forms';
33

44
import { BaseComponent } from '@shared/bases/base.component';
@@ -9,10 +9,12 @@ import { WebFormService } from '@shared/services/web-form.service';
99
templateUrl: 'jpeg-quality-level-control.component.html',
1010
styleUrls: ['jpeg-quality-level-control.component.scss'],
1111
})
12-
export class JpegQualityLevelControlComponent extends BaseComponent implements OnInit {
12+
export class JpegQualityLevelControlComponent extends BaseComponent implements OnInit, OnDestroy {
1313
@Input() parentForm: FormGroup;
1414
@Input() inputFormData;
1515

16+
@Output() controlReady = new EventEmitter<void>();
17+
1618
jpegEnabled = true;
1719

1820
constructor(private formService: WebFormService) {
@@ -24,20 +26,34 @@ export class JpegQualityLevelControlComponent extends BaseComponent implements O
2426
}
2527

2628
ngOnInit(): void {
27-
this.formService.addControlToForm({
28-
formGroup: this.parentForm,
29-
controlName: 'jpegEnabled',
30-
inputFormData: this.inputFormData,
31-
isRequired: false,
32-
defaultValue: true,
33-
});
34-
35-
this.formService.addControlToForm({
36-
formGroup: this.parentForm,
37-
controlName: 'jpegQualityLevel',
38-
inputFormData: this.inputFormData,
39-
isRequired: false,
40-
defaultValue: 9,
41-
});
29+
if (!this.parentForm.contains('jpegEnabled')) {
30+
this.formService.addControlToForm({
31+
formGroup: this.parentForm,
32+
controlName: 'jpegEnabled',
33+
inputFormData: this.inputFormData,
34+
isRequired: false,
35+
defaultValue: true,
36+
});
37+
38+
this.formService.addControlToForm({
39+
formGroup: this.parentForm,
40+
controlName: 'jpegQualityLevel',
41+
inputFormData: this.inputFormData,
42+
isRequired: false,
43+
defaultValue: 9,
44+
});
45+
46+
this.controlReady.emit();
47+
} else {
48+
this.parentForm.get('jpegEnabled').enable();
49+
}
50+
51+
this.jpegEnabled = this.parentForm.get('jpegEnabled').value;
52+
}
53+
54+
ngOnDestroy() {
55+
super.ngOnDestroy();
56+
// Disable the control to ignore it when reading the form. At the same time, the value is preserved.
57+
this.parentForm.get('jpegEnabled').disable();
4258
}
4359
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div [formGroup]="parentForm">
2+
<div class="gateway-form-input">
3+
<p-checkbox formControlName="pngEnabled"
4+
label="Use Tight PNG"
5+
binary="true"></p-checkbox>
6+
</div>
7+
</div>

0 commit comments

Comments
 (0)